Skip to content

Commit 4f5d13a

Browse files
frederick-vs-jacpplearnerStephanTLavavej
authored
Speculatively implement LWG-3857 (basic_string_view conversions) (#3348)
Co-authored-by: S. B. Tam <[email protected]> Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent c2b4352 commit 4f5d13a

File tree

6 files changed

+70
-15
lines changed

6 files changed

+70
-15
lines changed

stl/inc/format

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -853,13 +853,13 @@ _NODISCARD constexpr bool _Is_execution_charset_self_synchronizing() {
853853
#endif // ^^^ EDG workaround ^^^
854854
}
855855

856-
inline constexpr char32_t _Width_estimate_intervals[] = { // Per N4885 [format.string.std]/11
856+
inline constexpr char32_t _Width_estimate_intervals[] = { // Per N4928 [format.string.std]/12
857857
0x1100u, 0x1160u, 0x2329u, 0x232Bu, 0x2E80u, 0x303Fu, 0x3040u, 0xA4D0u, 0xAC00u, 0xD7A4u, 0xF900u, 0xFB00u, 0xFE10u,
858858
0xFE1Au, 0xFE30u, 0xFE70u, 0xFF00u, 0xFF61u, 0xFFE0u, 0xFFE7u, 0x1F300u, 0x1F650u, 0x1F900u, 0x1FA00u, 0x20000u,
859859
0x2FFFEu, 0x30000u, 0x3FFFEu};
860860

861861
_NODISCARD constexpr int _Unicode_width_estimate(const char32_t _Ch) noexcept {
862-
// Computes the width estimation for Unicode characters from N4885 [format.string.std]/11
862+
// Computes the width estimation for Unicode characters from N4928 [format.string.std]/12
863863
int _Result = 1;
864864
for (const auto& _Bound : _Width_estimate_intervals) {
865865
if (_Ch < _Bound) {
@@ -1731,13 +1731,13 @@ struct _Format_arg_traits {
17311731
using _Char_type = typename _Context::char_type;
17321732

17331733
// These overloads mirror the exposition-only single-argument constructor
1734-
// set of basic_format_arg (N4885 [format.arg]). They determine the mapping
1734+
// set of basic_format_arg (N4928 [format.arg]). They determine the mapping
17351735
// from "raw" to "erased" argument type for _Format_arg_store.
17361736
template <_Has_formatter<_Context> _Ty>
17371737
static auto _Phony_basic_format_arg_constructor(_Ty&&) {
17381738
// per the proposed resolution of LWG-3631
17391739
using _Td = remove_cvref_t<_Ty>;
1740-
// See N4885 [format.arg]/5
1740+
// See N4928 [format.arg]/5
17411741
if constexpr (is_same_v<_Td, bool>) {
17421742
return bool{};
17431743
} else if constexpr (is_same_v<_Td, _Char_type>) {
@@ -1878,7 +1878,15 @@ private:
18781878
_Arg_type = _Basic_format_arg_type::_Custom_type;
18791879
}
18801880

1881-
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, static_cast<_Erased_type>(_Val));
1881+
#if !_HAS_CXX23
1882+
// Workaround towards N4928 [format.arg]/9 and /10 in C++20
1883+
if constexpr (is_same_v<_Erased_type, basic_string_view<_CharType>>) {
1884+
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, _Erased_type{_Val.data(), _Val.size()});
1885+
} else
1886+
#endif // !_HAS_CXX23
1887+
{
1888+
_Store_impl<_Erased_type>(_Arg_index, _Arg_type, static_cast<_Erased_type>(_Val));
1889+
}
18821890
}
18831891

18841892
public:
@@ -3268,7 +3276,7 @@ struct _Format_handler {
32683276
};
32693277

32703278
// Generic formatter definition, the deleted default constructor
3271-
// makes it "disabled" as per N4885 [format.formatter.spec]/5
3279+
// makes it "disabled" as per N4928 [format.formatter.spec]/5
32723280
_EXPORT_STD template <class _Ty, class _CharT>
32733281
struct formatter {
32743282
formatter() = delete;
@@ -3397,15 +3405,15 @@ _EXPORT_STD template <class _Context = format_context, class... _Args>
33973405
_NODISCARD auto make_format_args(_Args&&... _Vals) {
33983406
static_assert((_Has_formatter<_Args, _Context> && ...),
33993407
"Cannot format an argument. To make type T formattable, provide a formatter<T> specialization. "
3400-
"See N4917 [format.arg.store]/2 and [formatter.requirements].");
3408+
"See N4928 [format.arg.store]/2 and [formatter.requirements].");
34013409
return _Format_arg_store<_Context, _Args...>{_Vals...};
34023410
}
34033411

34043412
_EXPORT_STD template <class... _Args>
34053413
_NODISCARD auto make_wformat_args(_Args&&... _Vals) {
34063414
static_assert((_Has_formatter<_Args, wformat_context> && ...),
34073415
"Cannot format an argument. To make type T formattable, provide a formatter<T> specialization. "
3408-
"See N4917 [format.arg.store]/2 and [formatter.requirements].");
3416+
"See N4928 [format.arg.store]/2 and [formatter.requirements].");
34093417
return _Format_arg_store<wformat_context, _Args...>{_Vals...};
34103418
}
34113419

stl/inc/xstring

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,10 +1260,7 @@ public:
12601260
&& (!is_convertible_v<_Range, const _Elem*>)
12611261
&& (!requires(remove_cvref_t<_Range>& _Rng) {
12621262
_Rng.operator _STD basic_string_view<_Elem, _Traits>();
1263-
})
1264-
&& (!requires {
1265-
typename remove_reference_t<_Range>::traits_type;
1266-
} || same_as<typename remove_reference_t<_Range>::traits_type, _Traits>))
1263+
})) // doesn't check member type traits_type, per LWG-3857
12671264
constexpr explicit basic_string_view(_Range&& _Rng) noexcept(
12681265
noexcept(_RANGES data(_Rng)) && noexcept(_RANGES size(_Rng))) // strengthened
12691266
: _Mydata(_RANGES data(_Rng)), _Mysize(static_cast<size_t>(_RANGES size(_Rng))) {}

tests/libcxx/expected_results.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,11 @@ std/language.support/support.limits/support.limits.general/format.version.compil
607607
# libc++ doesn't yet implement LWG-3670
608608
std/ranges/range.factories/range.iota.view/iterator/member_typedefs.compile.pass.cpp FAIL
609609

610+
# libc++ doesn't speculatively implement LWG-3857
611+
std/strings/string.view/string.view.cons/from_range.pass.cpp FAIL
612+
std/strings/string.view/string.view.cons/from_string1.compile.fail.cpp FAIL
613+
std/strings/string.view/string.view.cons/from_string2.compile.fail.cpp FAIL
614+
610615
# This test assumes that std::array<int, 4>::iterator is int*, but on MSVC STL it's _Array_iterator.
611616
# Other std/algorithm test failures likely have the same cause.
612617
std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp FAIL

tests/libcxx/skipped_tests.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,11 @@ language.support\support.limits\support.limits.general\format.version.compile.pa
607607
# libc++ doesn't yet implement LWG-3670
608608
ranges\range.factories\range.iota.view\iterator\member_typedefs.compile.pass.cpp
609609

610+
# libc++ doesn't speculatively implement LWG-3857
611+
strings\string.view\string.view.cons\from_range.pass.cpp
612+
strings\string.view\string.view.cons\from_string1.compile.fail.cpp
613+
strings\string.view\string.view.cons\from_string2.compile.fail.cpp
614+
610615
# This test assumes that std::array<int, 4>::iterator is int*, but on MSVC STL it's _Array_iterator.
611616
# Other std/algorithm test failures likely have the same cause.
612617
algorithms\alg.modifying.operations\alg.copy\ranges.copy.pass.cpp

tests/std/tests/P0220R1_string_view/test.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,18 @@ constexpr bool test_case_range_constructor() {
367367
static_assert(!is_constructible_v<string_view, vector<unsigned char>>); // different elements
368368
static_assert(!is_convertible_v<vector<unsigned char>, string_view>);
369369

370-
static_assert(!is_constructible_v<string_view, basic_string<char, constexpr_char_traits>>); // different traits
371-
static_assert(!is_convertible_v<basic_string<char, constexpr_char_traits>, string_view>);
370+
static_assert(!is_convertible_v<basic_string<char, constexpr_char_traits>, string_view>); // different traits
371+
static_assert(!is_convertible_v<basic_string_view<char, constexpr_char_traits>, string_view>);
372+
373+
static_assert(!is_convertible_v<string_view, basic_string_view<char, constexpr_char_traits>>);
374+
static_assert(!is_convertible_v<basic_string_view<char, constexpr_char_traits>, string>);
375+
376+
// LWG-3857 basic_string_view should allow explicit conversion when only traits vary
377+
static_assert(is_constructible_v<string_view, basic_string<char, constexpr_char_traits>>);
378+
static_assert(is_constructible_v<basic_string_view<char, constexpr_char_traits>, string_view>);
379+
380+
static_assert(is_constructible_v<string_view, basic_string_view<char, constexpr_char_traits>>);
381+
static_assert(is_constructible_v<basic_string_view<char, constexpr_char_traits>, string>);
372382
#endif // _HAS_CXX23 && defined(__cpp_lib_concepts)
373383

374384
return true;

tests/std/tests/P0645R10_text_formatting_formatting/test.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ constexpr auto llong_max = numeric_limits<long long>::max();
2525
constexpr auto ullong_max = numeric_limits<unsigned long long>::max();
2626

2727
// copied from the string_view tests
28-
template <typename CharT>
28+
template <class CharT>
2929
struct choose_literal; // not defined
3030

3131
template <>
@@ -52,6 +52,16 @@ struct choose_literal<wchar_t> {
5252
#define DEFAULT_IDL_SETTING 0
5353
#endif
5454

55+
// Test formatting basic_string(_view) with non-Standard traits_type
56+
template <class CharT>
57+
struct alternative_char_traits : char_traits<CharT> {};
58+
59+
template <class CharT>
60+
using alternative_basic_string_view = basic_string_view<CharT, alternative_char_traits<CharT>>;
61+
62+
template <class CharT, class Alloc = allocator<CharT>>
63+
using alternative_basic_string = basic_string<CharT, alternative_char_traits<CharT>, Alloc>;
64+
5565
template <class charT, class... Args>
5666
auto make_testing_format_args(Args&&... vals) {
5767
if constexpr (is_same_v<charT, wchar_t>) {
@@ -136,6 +146,26 @@ void test_simple_formatting() {
136146
0.0, STR("s"), basic_string_view{STR("sv")}, nullptr, static_cast<void*>(nullptr));
137147
assert(output_string == STR("true a 0 0 0 s sv 0x0 0x0"));
138148

149+
// Test formatting basic_string(_view) with non-Standard traits_type
150+
// TRANSITION, LLVM-54051, DevCom-10255929, should also test class template argument deduction for alias templates
151+
output_string.clear();
152+
format_to(move_only_back_inserter{output_string}, STR("{} {} {} {} {} {} {} {} {} {}"), true, charT{'a'}, 0, 0u,
153+
0.0, STR("s"), alternative_basic_string<charT>{STR("str")}, alternative_basic_string_view<charT>{STR("sv")},
154+
nullptr, static_cast<void*>(nullptr));
155+
assert(output_string == STR("true a 0 0 0 s str sv 0x0 0x0"));
156+
157+
output_string.clear();
158+
format_to(move_only_back_inserter{output_string}, STR("{:} {:} {:} {:} {:} {:} {:} {:} {:} {:}"), true, charT{'a'},
159+
0, 0u, 0.0, STR("s"), alternative_basic_string<charT>{STR("str")},
160+
alternative_basic_string_view<charT>{STR("sv")}, nullptr, static_cast<void*>(nullptr));
161+
assert(output_string == STR("true a 0 0 0 s str sv 0x0 0x0"));
162+
163+
output_string.clear();
164+
format_to_n(move_only_back_inserter{output_string}, 300, STR("{} {} {} {} {} {} {} {} {} {}"), true, charT{'a'}, 0,
165+
0u, 0.0, STR("s"), alternative_basic_string<charT>{STR("str")}, alternative_basic_string_view<charT>{STR("sv")},
166+
nullptr, static_cast<void*>(nullptr));
167+
assert(output_string == STR("true a 0 0 0 s str sv 0x0 0x0"));
168+
139169
output_string.clear();
140170
vformat_to(
141171
back_insert_iterator{output_string}, locale::classic(), STR("format"), make_testing_format_args<charT>());

0 commit comments

Comments
 (0)