| //===----------------------------------------------------------------------===// |
| // |
| // 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 |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H |
| #define SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H |
| |
| #include <algorithm> |
| #include <array> |
| #include <cassert> |
| #include <cstddef> |
| #include <iterator> |
| #include <ranges> |
| #include <utility> |
| |
| #include "../exception_safety_helpers.h" |
| #include "../from_range_helpers.h" |
| #include "MoveOnly.h" |
| #include "almost_satisfies_types.h" |
| #include "count_new.h" |
| #include "test_iterators.h" |
| #include "test_macros.h" |
| |
| template <class T> |
| concept HasSize = requires (const T& value) { value.size(); }; |
| |
| template <class Container, class Range> |
| concept HasFromRangeCtr = requires (Range&& range) { |
| Container(std::from_range, std::forward<Range>(range)); |
| Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>()); |
| }; |
| |
| template <template <class...> class Container, class T, class U> |
| constexpr bool test_constraints() { |
| // Input range with the same value type. |
| static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>); |
| // Input range with a convertible value type. |
| static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>); |
| // Input range with a non-convertible value type. |
| static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>); |
| // Not an input range. |
| static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>); |
| static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>); |
| static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>); |
| |
| // Note: there are no constraints on the allocator (it's not a separate template type of the constructor)`. |
| |
| return true; |
| } |
| |
| // Note: `std::array` is used to avoid dealing with `vector<bool>`. |
| template <template <class ...> class Container, |
| class T, |
| class Iter, |
| class Sent, |
| class Alloc, |
| std::size_t N, |
| class ValidateFunc> |
| constexpr void test_sequence_container_with_input(std::array<T, N>&& input, ValidateFunc validate) { |
| auto in = wrap_input<Iter, Sent>(input); |
| |
| { // (range) |
| Container<T> c(std::from_range, in); |
| |
| if constexpr (HasSize<Container<T>>) { |
| assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
| } |
| assert(std::ranges::equal(in, c)); |
| validate(c); |
| } |
| |
| { // (range, allocator) |
| Alloc alloc; |
| Container<T, Alloc> c(std::from_range, in, alloc); |
| |
| assert(c.get_allocator() == alloc); |
| if constexpr (HasSize<Container<T, Alloc>>) { |
| assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end()))); |
| } |
| assert(std::ranges::equal(in, c)); |
| validate(c); |
| } |
| } |
| |
| template <template <class ...> class Container, |
| class T, |
| class Iter, |
| class Sent, |
| class Alloc, |
| class ValidateFunc> |
| constexpr void test_sequence_container(ValidateFunc validate) { |
| // Normal input. |
| test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{0, 5, 12, 7, -1, 8, 26}, validate); |
| // Empty input. |
| test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array<int, 0>{}, validate); |
| // Single-element input. |
| test_sequence_container_with_input<Container, T, Iter, Sent, Alloc>(std::array{5}, validate); |
| } |
| |
| template <template <class ...> class Container> |
| constexpr void test_sequence_container_move_only() { |
| MoveOnly input[5]; |
| std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5}); |
| |
| [[maybe_unused]] Container<MoveOnly> c(std::from_range, in); |
| } |
| |
| template <class Iter, |
| class Sent, |
| class Alloc, |
| class ValidateFunc> |
| constexpr void test_vector_bool(ValidateFunc validate) { |
| // Normal input. |
| test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>( |
| std::array{true, false, false, true, false, true, true, true, false, true}, validate); |
| // Empty input. |
| test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array<bool, 0>{}, validate); |
| // Single-element input. |
| test_sequence_container_with_input<std::vector, bool, Iter, Sent, Alloc>(std::array{true}, validate); |
| } |
| |
| template <template <class ...> class Container> |
| void test_exception_safety_throwing_copy() { |
| #if !defined(TEST_HAS_NO_EXCEPTIONS) |
| constexpr int ThrowOn = 3; |
| using T = ThrowingCopy<ThrowOn>; |
| test_exception_safety_throwing_copy<ThrowOn, /*Size=*/5>([](T* from, T* to) { |
| [[maybe_unused]] Container<T> c(std::from_range, std::ranges::subrange(from, to)); |
| }); |
| #endif |
| } |
| |
| template <template <class ...> class Container, class T> |
| void test_exception_safety_throwing_allocator() { |
| #if !defined(TEST_HAS_NO_EXCEPTIONS) |
| T in[] = {0, 1}; |
| |
| try { |
| ThrowingAllocator<T> alloc; |
| |
| globalMemCounter.reset(); |
| Container<T, ThrowingAllocator<T>> c(std::from_range, in, alloc); |
| assert(false); // The constructor call above should throw. |
| |
| } catch (int) { |
| assert(globalMemCounter.new_called == globalMemCounter.delete_called); |
| } |
| #endif |
| } |
| |
| #endif // SUPPORT_FROM_RANGE_SEQUENCE_CONTAINERS_H |