[libcxx-commits] [libcxx] 34206b8 - [libc++] Overhaul std::quoted; fix its relationship to character traits.
Arthur O'Dwyer via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Mar 7 10:29:08 PST 2022
Author: Arthur O'Dwyer
Date: 2022-03-07T13:28:58-05:00
New Revision: 34206b869d0d4533aa6a2f18071563fe40945869
URL: https://github.com/llvm/llvm-project/commit/34206b869d0d4533aa6a2f18071563fe40945869
DIFF: https://github.com/llvm/llvm-project/commit/34206b869d0d4533aa6a2f18071563fe40945869.diff
LOG: [libc++] Overhaul std::quoted; fix its relationship to character traits.
Move `__quoted_output_proxy` into the one file that uses it.
A `const char*` has no associated traits class, so `std::quoted("literal")`
should be printable into any basic_ostream regardless of traits.
Use hidden-friend `operator<<` and `operator>>`, since we're permitted to.
(The exact signature is unspecified because the class itself is unspecified.)
We shouldn't support `std::quoted("literal")` in C++03 or C++11 mode.
(We do need `std::__quoted(s)` and `std::__quoted(cs)` in C++11 mode,
because they're used by `std::__fs::filesystem::path`.)
Differential Revision: https://reviews.llvm.org/D120135
Added:
libcxx/test/std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp
Modified:
libcxx/docs/ReleaseNotes.rst
libcxx/include/__string
libcxx/include/iomanip
Removed:
################################################################################
diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 34fd16bee9931..19cc2da6331b8 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -66,6 +66,9 @@ API Changes
(as an extension) ``__int128_t``, and the unsigned versions thereof.
In particular, ``uniform_int_distribution<int8_t>`` is no longer supported.
+- The C++14 function ``std::quoted(const char*)`` is no longer supported in
+ C++03 or C++11 modes.
+
ABI Changes
-----------
diff --git a/libcxx/include/__string b/libcxx/include/__string
index 203246c4a9909..973b6994a5c2f 100644
--- a/libcxx/include/__string
+++ b/libcxx/include/__string
@@ -1155,19 +1155,6 @@ size_t __do_string_hash(_Ptr __p, _Ptr __e)
return __murmur2_or_cityhash<size_t>()(__p, (__e-__p)*sizeof(value_type));
}
-template <class _CharT, class _Iter, class _Traits = char_traits<_CharT> >
-struct __quoted_output_proxy
-{
- _Iter __first_;
- _Iter __last_;
- _CharT __delim_;
- _CharT __escape_;
-
- _LIBCPP_HIDE_FROM_ABI
- explicit __quoted_output_proxy(_Iter __f, _Iter __l, _CharT __d, _CharT __e)
- : __first_(__f), __last_(__l), __delim_(__d), __escape_(__e) {}
-};
-
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
diff --git a/libcxx/include/iomanip b/libcxx/include/iomanip
index f34c718b09b4c..c4d8351926b66 100644
--- a/libcxx/include/iomanip
+++ b/libcxx/include/iomanip
@@ -513,10 +513,12 @@ put_time(const tm* __tm, const _CharT* __fmt)
return __iom_t10<_CharT>(__tm, __fmt);
}
-template <class _CharT, class _Traits, class _ForwardIterator>
-basic_ostream<_CharT, _Traits>&
+#if _LIBCPP_STD_VER >= 11
+
+template <class _CharT, class _Traits>
+_LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
__quoted_output(basic_ostream<_CharT, _Traits>& __os,
- _ForwardIterator __first, _ForwardIterator __last, _CharT __delim, _CharT __escape)
+ const _CharT *__first, const _CharT *__last, _CharT __delim, _CharT __escape)
{
basic_string<_CharT, _Traits> __str;
__str.push_back(__delim);
@@ -530,7 +532,7 @@ __quoted_output(basic_ostream<_CharT, _Traits>& __os,
}
template <class _CharT, class _Traits, class _String>
-basic_istream<_CharT, _Traits>&
+_LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
__quoted_input(basic_istream<_CharT, _Traits>& __is, _String& __string, _CharT __delim, _CharT __escape)
{
__string.clear();
@@ -546,7 +548,7 @@ __quoted_input(basic_istream<_CharT, _Traits>& __is, _String& __string, _CharT _
return __is;
}
- __save_flags<_CharT, _Traits> sf(__is);
+ __save_flags<_CharT, _Traits> __sf(__is);
std::noskipws(__is);
while (true) {
__is >> __c;
@@ -563,16 +565,27 @@ __quoted_input(basic_istream<_CharT, _Traits>& __is, _String& __string, _CharT _
return __is;
}
-template <class _CharT, class _Traits, class _Iter>
-basic_ostream<_CharT, _Traits>& operator<<(
- basic_ostream<_CharT, _Traits>& __os,
- const __quoted_output_proxy<_CharT, _Iter, _Traits>& __proxy)
+template <class _CharT, class _Traits>
+struct _LIBCPP_HIDDEN __quoted_output_proxy
{
- return std::__quoted_output(__os, __proxy.__first_, __proxy.__last_, __proxy.__delim_, __proxy.__escape_);
-}
+ const _CharT *__first_;
+ const _CharT *__last_;
+ _CharT __delim_;
+ _CharT __escape_;
+
+ _LIBCPP_HIDE_FROM_ABI
+ explicit __quoted_output_proxy(const _CharT *__f, const _CharT *__l, _CharT __d, _CharT __e)
+ : __first_(__f), __last_(__l), __delim_(__d), __escape_(__e) {}
+
+ template<class _T2, __enable_if_t<_IsSame<_Traits, void>::value || _IsSame<_Traits, _T2>::value>* = nullptr>
+ friend _LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _T2>&
+ operator<<(basic_ostream<_CharT, _T2>& __os, const __quoted_output_proxy& __p) {
+ return std::__quoted_output(__os, __p.__first_, __p.__last_, __p.__delim_, __p.__escape_);
+ }
+};
template <class _CharT, class _Traits, class _Allocator>
-struct __quoted_proxy
+struct _LIBCPP_HIDDEN __quoted_proxy
{
basic_string<_CharT, _Traits, _Allocator>& __string_;
_CharT __delim_;
@@ -581,76 +594,66 @@ struct __quoted_proxy
_LIBCPP_HIDE_FROM_ABI
explicit __quoted_proxy(basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __d, _CharT __e)
: __string_(__s), __delim_(__d), __escape_(__e) {}
+
+ friend _LIBCPP_HIDE_FROM_ABI basic_ostream<_CharT, _Traits>&
+ operator<<(basic_ostream<_CharT, _Traits>& __os, const __quoted_proxy& __p) {
+ return std::__quoted_output(__os, __p.__string_.data(), __p.__string_.data() + __p.__string_.size(), __p.__delim_, __p.__escape_);
+ }
+
+ friend _LIBCPP_HIDE_FROM_ABI basic_istream<_CharT, _Traits>&
+ operator>>(basic_istream<_CharT, _Traits>& __is, const __quoted_proxy& __p) {
+ return std::__quoted_input(__is, __p.__string_, __p.__delim_, __p.__escape_);
+ }
};
template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-basic_ostream<_CharT, _Traits>& operator<<(
- basic_ostream<_CharT, _Traits>& __os,
- const __quoted_proxy<_CharT, _Traits, _Allocator>& __proxy)
+_LIBCPP_HIDE_FROM_ABI
+__quoted_output_proxy<_CharT, _Traits>
+__quoted(const basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
- return std::__quoted_output(__os, __proxy.__string_.cbegin(), __proxy.__string_.cend(), __proxy.__delim_, __proxy.__escape_);
+ return __quoted_output_proxy<_CharT, _Traits>(__s.data(), __s.data() + __s.size(), __delim, __escape);
}
-// extractor for non-const basic_string& proxies
template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-basic_istream<_CharT, _Traits>& operator>>(
- basic_istream<_CharT, _Traits>& __is,
- const __quoted_proxy<_CharT, _Traits, _Allocator>& __proxy)
+_LIBCPP_HIDE_FROM_ABI
+__quoted_proxy<_CharT, _Traits, _Allocator>
+__quoted(basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
- return std::__quoted_input(__is, __proxy.__string_, __proxy.__delim_, __proxy.__escape_);
+ return __quoted_proxy<_CharT, _Traits, _Allocator>(__s, __delim, __escape);
}
+#endif // _LIBCPP_STD_VER >= 11
+
+#if _LIBCPP_STD_VER > 11
+
template <class _CharT>
-_LIBCPP_INLINE_VISIBILITY
-__quoted_output_proxy<_CharT, const _CharT *>
-quoted(const _CharT *__s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
+_LIBCPP_HIDE_FROM_ABI
+auto quoted(const _CharT *__s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
const _CharT *__end = __s;
while (*__end) ++__end;
- return __quoted_output_proxy<_CharT, const _CharT *>(__s, __end, __delim, __escape);
+ return __quoted_output_proxy<_CharT, void>(__s, __end, __delim, __escape);
}
template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-__quoted_output_proxy<_CharT, typename basic_string <_CharT, _Traits, _Allocator>::const_iterator>
-__quoted(const basic_string <_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
+_LIBCPP_HIDE_FROM_ABI
+auto quoted(const basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
- return __quoted_output_proxy<_CharT, typename basic_string<_CharT, _Traits, _Allocator>::const_iterator>(__s.cbegin(), __s.cend(), __delim, __escape);
+ return __quoted_output_proxy<_CharT, _Traits>(__s.data(), __s.data() + __s.size(), __delim, __escape);
}
template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-__quoted_proxy<_CharT, _Traits, _Allocator>
-__quoted(basic_string <_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
+_LIBCPP_HIDE_FROM_ABI
+auto quoted(basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
return __quoted_proxy<_CharT, _Traits, _Allocator>(__s, __delim, __escape);
}
-#if _LIBCPP_STD_VER > 11
-
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-__quoted_output_proxy<_CharT, typename basic_string<_CharT, _Traits, _Allocator>::const_iterator>
-quoted(const basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
-{
- return std::__quoted(__s, __delim, __escape);
-}
-
-template <class _CharT, class _Traits, class _Allocator>
-_LIBCPP_INLINE_VISIBILITY
-__quoted_proxy<_CharT, _Traits, _Allocator>
-quoted(basic_string<_CharT, _Traits, _Allocator>& __s, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
-{
- return std::__quoted(__s, __delim, __escape);
-}
-
template <class _CharT, class _Traits>
-__quoted_output_proxy<_CharT, const _CharT *, _Traits>
-quoted(basic_string_view<_CharT, _Traits> __sv, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
+_LIBCPP_HIDE_FROM_ABI
+auto quoted(basic_string_view<_CharT, _Traits> __sv, _CharT __delim = _CharT('"'), _CharT __escape = _CharT('\\'))
{
- return __quoted_output_proxy<_CharT, const _CharT *, _Traits>(__sv.data(), __sv.data() + __sv.size(), __delim, __escape);
+ return __quoted_output_proxy<_CharT, _Traits>(__sv.data(), __sv.data() + __sv.size(), __delim, __escape);
}
#endif // _LIBCPP_STD_VER > 11
diff --git a/libcxx/test/std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp b/libcxx/test/std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp
new file mode 100644
index 0000000000000..35aa54b1772b3
--- /dev/null
+++ b/libcxx/test/std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp
@@ -0,0 +1,166 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11
+
+// <iomanip>
+
+// std::quoted
+// Verify that the result type of std::quoted can be streamed to
+// (and from) ostreams with the correct CharTraits, and not those
+// with the wrong CharTraits. To avoid our having to create working
+// ostreams with weird CharTraits, this is a compile-only test.
+
+#include <iomanip>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <string_view>
+#include <type_traits>
+
+#include "test_allocator.h"
+#include "test_macros.h"
+
+template<class IS, class Q>
+decltype(std::declval<IS>() >> std::declval<Q>(), std::true_type())
+has_rightshift_impl(int) { return std::true_type(); }
+
+template<class IS, class Q>
+std::false_type
+has_rightshift_impl(long) { return std::false_type(); }
+
+template<class IS, class Q>
+struct HasRightShift : decltype(has_rightshift_impl<IS, Q>(0)) {};
+
+template<class OS, class Q>
+decltype(std::declval<OS>() << std::declval<Q>(), std::true_type())
+has_leftshift_impl(int) { return std::true_type(); }
+
+template<class OS, class Q>
+std::false_type
+has_leftshift_impl(long) { return std::false_type(); }
+
+template<class OS, class Q>
+struct HasLeftShift : decltype(has_leftshift_impl<OS, Q>(0)) {};
+
+template<class CharT>
+struct FakeCharTraits : std::char_traits<CharT> {};
+
+void test_string_literal()
+{
+ using Q = decltype(std::quoted("hello"));
+ static_assert( HasLeftShift<std::ostream&, Q>::value, "");
+ static_assert(!HasRightShift<std::istream&, Q>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, Q>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, Q>::value, "");
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ using WQ = decltype(std::quoted(L"hello"));
+ static_assert( HasLeftShift<std::wostream&, WQ>::value, "");
+ static_assert(!HasRightShift<std::wistream&, WQ>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+
+ static_assert(!HasLeftShift<std::ostream&, WQ>::value, "");
+ static_assert(!HasLeftShift<std::wostream&, Q>::value, "");
+#endif // TEST_HAS_NO_WIDE_CHARACTERS
+}
+
+void test_std_string()
+{
+ std::string s = "hello";
+ const auto& cs = s;
+ using Q = decltype(std::quoted(s));
+ using CQ = decltype(std::quoted(cs));
+ static_assert( HasLeftShift<std::ostream&, Q>::value, "");
+ static_assert( HasRightShift<std::istream&, Q>::value, "");
+ static_assert( HasLeftShift<std::ostream&, CQ>::value, "");
+ static_assert(!HasRightShift<std::istream&, CQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, Q>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, Q>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, CQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, CQ>::value, "");
+
+ std::basic_string<char, FakeCharTraits<char>, test_allocator<char>> st = "hello";
+ const auto& cst = st;
+ using QT = decltype(std::quoted(st));
+ using CQT = decltype(std::quoted(cst));
+ static_assert(!HasLeftShift<std::ostream&, QT>::value, "");
+ static_assert(!HasRightShift<std::istream&, QT>::value, "");
+ static_assert(!HasLeftShift<std::ostream&, CQT>::value, "");
+ static_assert(!HasRightShift<std::istream&, CQT>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, QT>::value, "");
+ static_assert( HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, QT>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, CQT>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, CQT>::value, "");
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ std::wstring ws = L"hello";
+ const auto& cws = ws;
+ using WQ = decltype(std::quoted(ws));
+ using CWQ = decltype(std::quoted(cws));
+ static_assert( HasLeftShift<std::wostream&, WQ>::value, "");
+ static_assert( HasRightShift<std::wistream&, WQ>::value, "");
+ static_assert( HasLeftShift<std::wostream&, CWQ>::value, "");
+ static_assert(!HasRightShift<std::wistream&, CWQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<wchar_t, FakeCharTraits<wchar_t>>&, CWQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<wchar_t, FakeCharTraits<wchar_t>>&, CWQ>::value, "");
+
+ static_assert(!HasLeftShift<std::ostream&, WQ>::value, "");
+ static_assert(!HasLeftShift<std::wostream&, Q>::value, "");
+#endif // TEST_HAS_NO_WIDE_CHARACTERS
+}
+
+void test_std_string_view()
+{
+ std::string_view s = "hello";
+ const auto& cs = s;
+ using Q = decltype(std::quoted(s));
+ using CQ = decltype(std::quoted(cs));
+ static_assert( HasLeftShift<std::ostream&, Q>::value, "");
+ static_assert(!HasRightShift<std::istream&, Q>::value, "");
+ static_assert( HasLeftShift<std::ostream&, CQ>::value, "");
+ static_assert(!HasRightShift<std::istream&, CQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, Q>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, Q>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, CQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, CQ>::value, "");
+
+ std::basic_string_view<char, FakeCharTraits<char>> st = "hello";
+ const auto& cst = st;
+ using QT = decltype(std::quoted(st));
+ using CQT = decltype(std::quoted(cst));
+ static_assert(!HasLeftShift<std::ostream&, QT>::value, "");
+ static_assert(!HasRightShift<std::istream&, QT>::value, "");
+ static_assert(!HasLeftShift<std::ostream&, CQT>::value, "");
+ static_assert(!HasRightShift<std::istream&, CQT>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, QT>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, QT>::value, "");
+ static_assert( HasLeftShift<std::basic_ostream<char, FakeCharTraits<char>>&, CQT>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<char, FakeCharTraits<char>>&, CQT>::value, "");
+
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ std::wstring_view ws = L"hello";
+ const auto& cws = ws;
+ using WQ = decltype(std::quoted(ws));
+ using CWQ = decltype(std::quoted(cws));
+ static_assert( HasLeftShift<std::wostream&, WQ>::value, "");
+ static_assert(!HasRightShift<std::wistream&, WQ>::value, "");
+ static_assert( HasLeftShift<std::wostream&, CWQ>::value, "");
+ static_assert(!HasRightShift<std::wistream&, CWQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<wchar_t, FakeCharTraits<wchar_t>>&, WQ>::value, "");
+ static_assert(!HasLeftShift<std::basic_ostream<wchar_t, FakeCharTraits<wchar_t>>&, CWQ>::value, "");
+ static_assert(!HasRightShift<std::basic_istream<wchar_t, FakeCharTraits<wchar_t>>&, CWQ>::value, "");
+
+ static_assert(!HasLeftShift<std::ostream&, WQ>::value, "");
+ static_assert(!HasLeftShift<std::wostream&, Q>::value, "");
+#endif // TEST_HAS_NO_WIDE_CHARACTERS
+}
More information about the libcxx-commits
mailing list