1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 10 11 // template<container-compatible-range<charT> R> 12 // constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23 13 14 #include <algorithm> 15 #include <sstream> 16 #include <string> 17 #include <utility> 18 #include <vector> 19 20 #include "../../../containers/from_range_helpers.h" 21 #include "../../../containers/sequences/from_range_sequence_containers.h" 22 #include "test_macros.h" 23 #include "asan_testing.h" 24 25 template <class Container, class Range, class Alloc> 26 concept StringHasFromRangeAllocCtr = 27 requires(Range&& range, const Alloc& alloc) { Container(std::from_range, std::forward<Range>(range), alloc); }; 28 29 constexpr bool test_constraints() { 30 // (from_range, range) 31 // 32 // Input range with the same value type. 33 static_assert(HasFromRangeCtr<std::string, InputRange<char>>); 34 // Input range with a convertible value type. 35 static_assert(HasFromRangeCtr<std::string, InputRange<int>>); 36 // Input range with a non-convertible value type. 37 static_assert(!HasFromRangeCtr<std::string, InputRange<Empty>>); 38 // Not an input range. 39 static_assert(!HasFromRangeCtr<std::string, InputRangeNotDerivedFrom>); 40 static_assert(!HasFromRangeCtr<std::string, InputRangeNotIndirectlyReadable>); 41 static_assert(!HasFromRangeCtr<std::string, InputRangeNotInputOrOutputIterator>); 42 43 // (from_range, range, alloc) 44 // 45 // Input range with the same value type. 46 using Alloc = test_allocator<char>; 47 using StringWithAlloc = std::basic_string<char, std::char_traits<char>, Alloc>; 48 static_assert(StringHasFromRangeAllocCtr<StringWithAlloc, InputRange<char>, Alloc>); 49 // Input range with a convertible value type. 50 static_assert(StringHasFromRangeAllocCtr<StringWithAlloc, InputRange<int>, Alloc>); 51 // Input range with a non-convertible value type. 52 static_assert(!StringHasFromRangeAllocCtr<StringWithAlloc, InputRange<Empty>, Alloc>); 53 // Not an input range. 54 static_assert(!StringHasFromRangeAllocCtr<StringWithAlloc, InputRangeNotDerivedFrom, Alloc>); 55 static_assert(!StringHasFromRangeAllocCtr<StringWithAlloc, InputRangeNotIndirectlyReadable, Alloc>); 56 static_assert(!StringHasFromRangeAllocCtr<StringWithAlloc, InputRangeNotInputOrOutputIterator, Alloc>); 57 // Not an allocator. 58 static_assert(!StringHasFromRangeAllocCtr<StringWithAlloc, InputRange<char>, Empty>); 59 60 return true; 61 } 62 63 template <class Iter, class Sent, class Alloc> 64 constexpr void test_with_input(std::vector<char> input) { 65 { // (range) 66 std::ranges::subrange in(Iter(input.data()), Sent(Iter(input.data() + input.size()))); 67 std::string c(std::from_range, in); 68 69 LIBCPP_ASSERT(c.__invariants()); 70 assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); 71 assert(std::ranges::equal(input, c)); 72 LIBCPP_ASSERT(is_string_asan_correct(c)); 73 } 74 75 { // (range, allocator) 76 std::ranges::subrange in(Iter(input.data()), Sent(Iter(input.data() + input.size()))); 77 Alloc alloc; 78 std::basic_string<char, std::char_traits<char>, Alloc> c(std::from_range, in, alloc); 79 80 LIBCPP_ASSERT(c.__invariants()); 81 assert(c.get_allocator() == alloc); 82 assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); 83 assert(std::ranges::equal(input, c)); 84 LIBCPP_ASSERT(is_string_asan_correct(c)); 85 } 86 87 { // Ensure input-only sized ranges are accepted. 88 using input_iter = cpp20_input_iterator<const char*>; 89 const char in[]{'q', 'w', 'e', 'r'}; 90 std::string s(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in))); 91 assert(s == "qwer"); 92 } 93 } 94 95 void test_string_exception_safety_throwing_allocator() { 96 #if !defined(TEST_HAS_NO_EXCEPTIONS) 97 try { 98 ThrowingAllocator<char> alloc; 99 100 globalMemCounter.reset(); 101 // Note: the input string must be long enough to prevent SSO, otherwise the allocator won't be used. 102 std::basic_string<char, std::char_traits<char>, ThrowingAllocator<char>> c( 103 std::from_range, std::vector<char>(64, 'A'), alloc); 104 assert(false); // The constructor call should throw. 105 106 } catch (int) { 107 assert(globalMemCounter.new_called == globalMemCounter.delete_called); 108 } 109 #endif 110 } 111 112 constexpr bool test_inputs() { 113 for_all_iterators_and_allocators<char>([]<class Iter, class Sent, class Alloc>() { 114 // Shorter input -- SSO. 115 test_with_input<Iter, Sent, Alloc>({'a', 'b', 'c', 'd', 'e'}); 116 // Longer input -- no SSO. 117 test_with_input<Iter, Sent, Alloc>(std::vector<char>(64, 'A')); 118 // Empty input. 119 test_with_input<Iter, Sent, Alloc>({}); 120 // Single-element input. 121 test_with_input<Iter, Sent, Alloc>({'a'}); 122 }); 123 124 return true; 125 } 126 127 #ifndef TEST_HAS_NO_LOCALIZATION 128 void test_counted_istream_view() { 129 std::istringstream is{"qwert"}; 130 auto vals = std::views::istream<char>(is); 131 std::string s(std::from_range, std::views::counted(vals.begin(), 3)); 132 assert(s == "qwe"); 133 } 134 #endif 135 136 int main(int, char**) { 137 test_inputs(); 138 static_assert(test_inputs()); 139 140 static_assert(test_constraints()); 141 142 // Note: `test_exception_safety_throwing_copy` doesn't apply because copying a `char` cannot throw. 143 test_string_exception_safety_throwing_allocator(); 144 145 #ifndef TEST_HAS_NO_LOCALIZATION 146 test_counted_istream_view(); 147 #endif 148 149 return 0; 150 } 151