From 34206b869d0d4533aa6a2f18071563fe40945869 Mon Sep 17 00:00:00 2001 From: Arthur O'Dwyer Date: Fri, 4 Mar 2022 18:24:18 -0500 Subject: [PATCH] [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 --- libcxx/docs/ReleaseNotes.rst | 3 + libcxx/include/__string | 13 -- libcxx/include/iomanip | 113 +++++++------- .../quoted.manip/quoted_traits.compile.pass.cpp | 166 +++++++++++++++++++++ 4 files changed, 227 insertions(+), 68 deletions(-) create mode 100644 libcxx/test/std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst index 34fd16b..19cc2da 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`` 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 203246c..973b699 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()(__p, (__e-__p)*sizeof(value_type)); } -template > -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 f34c718..c4d8351 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 -basic_ostream<_CharT, _Traits>& +#if _LIBCPP_STD_VER >= 11 + +template +_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 -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 -basic_ostream<_CharT, _Traits>& operator<<( - basic_ostream<_CharT, _Traits>& __os, - const __quoted_output_proxy<_CharT, _Iter, _Traits>& __proxy) +template +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::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 -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 -_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 -_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 -_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 -_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 -_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 -_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 -_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 -__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 0000000..35aa54b --- /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 + +// + +// 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 +#include +#include +#include +#include +#include + +#include "test_allocator.h" +#include "test_macros.h" + +template +decltype(std::declval() >> std::declval(), std::true_type()) +has_rightshift_impl(int) { return std::true_type(); } + +template +std::false_type +has_rightshift_impl(long) { return std::false_type(); } + +template +struct HasRightShift : decltype(has_rightshift_impl(0)) {}; + +template +decltype(std::declval() << std::declval(), std::true_type()) +has_leftshift_impl(int) { return std::true_type(); } + +template +std::false_type +has_leftshift_impl(long) { return std::false_type(); } + +template +struct HasLeftShift : decltype(has_leftshift_impl(0)) {}; + +template +struct FakeCharTraits : std::char_traits {}; + +void test_string_literal() +{ + using Q = decltype(std::quoted("hello")); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift>&, Q>::value, ""); + static_assert(!HasRightShift>&, Q>::value, ""); + +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + using WQ = decltype(std::quoted(L"hello")); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift>&, WQ>::value, ""); + static_assert(!HasRightShift>&, WQ>::value, ""); + + static_assert(!HasLeftShift::value, ""); + static_assert(!HasLeftShift::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::value, ""); + static_assert( HasRightShift::value, ""); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift>&, Q>::value, ""); + static_assert(!HasRightShift>&, Q>::value, ""); + static_assert(!HasLeftShift>&, CQ>::value, ""); + static_assert(!HasRightShift>&, CQ>::value, ""); + + std::basic_string, test_allocator> st = "hello"; + const auto& cst = st; + using QT = decltype(std::quoted(st)); + using CQT = decltype(std::quoted(cst)); + static_assert(!HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift>&, QT>::value, ""); + static_assert( HasRightShift>&, QT>::value, ""); + static_assert( HasLeftShift>&, CQT>::value, ""); + static_assert(!HasRightShift>&, 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::value, ""); + static_assert( HasRightShift::value, ""); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift>&, WQ>::value, ""); + static_assert(!HasRightShift>&, WQ>::value, ""); + static_assert(!HasLeftShift>&, CWQ>::value, ""); + static_assert(!HasRightShift>&, CWQ>::value, ""); + + static_assert(!HasLeftShift::value, ""); + static_assert(!HasLeftShift::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::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift>&, Q>::value, ""); + static_assert(!HasRightShift>&, Q>::value, ""); + static_assert(!HasLeftShift>&, CQ>::value, ""); + static_assert(!HasRightShift>&, CQ>::value, ""); + + std::basic_string_view> st = "hello"; + const auto& cst = st; + using QT = decltype(std::quoted(st)); + using CQT = decltype(std::quoted(cst)); + static_assert(!HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift>&, QT>::value, ""); + static_assert(!HasRightShift>&, QT>::value, ""); + static_assert( HasLeftShift>&, CQT>::value, ""); + static_assert(!HasRightShift>&, 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::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert( HasLeftShift::value, ""); + static_assert(!HasRightShift::value, ""); + static_assert(!HasLeftShift>&, WQ>::value, ""); + static_assert(!HasRightShift>&, WQ>::value, ""); + static_assert(!HasLeftShift>&, CWQ>::value, ""); + static_assert(!HasRightShift>&, CWQ>::value, ""); + + static_assert(!HasLeftShift::value, ""); + static_assert(!HasLeftShift::value, ""); +#endif // TEST_HAS_NO_WIDE_CHARACTERS +} -- 2.7.4