blob: 352ee474cae7672712daea89f6ec00b70d3854e2 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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_INSERT_RANGE_SEQUENCE_CONTAINERS_H
#define SUPPORT_INSERT_RANGE_SEQUENCE_CONTAINERS_H
#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../exception_safety_helpers.h"
#include "../from_range_helpers.h"
#include "../insert_range_helpers.h"
#include "MoveOnly.h"
#include "almost_satisfies_types.h"
#include "count_new.h"
#include "min_allocator.h"
#include "test_allocator.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"
template <class Container, class Range>
concept HasInsertRange = requires (Container& c, Range&& range) {
c.insert_range(c.end(), range);
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_insert_range() {
// Input range with the same value type.
static_assert(HasInsertRange<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasInsertRange<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasInsertRange<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasInsertRange<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasInsertRange<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasInsertRange<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
template <class Container, class Range>
concept HasAppendRange = requires (Container& c, Range&& range) {
c.append_range(range);
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_append_range() {
// Input range with the same value type.
static_assert(HasAppendRange<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasAppendRange<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasAppendRange<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasAppendRange<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasAppendRange<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasAppendRange<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
template <class Container, class Range>
concept HasPrependRange = requires (Container& c, Range&& range) {
c.prepend_range(range);
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_prepend_range() {
// Input range with the same value type.
static_assert(HasPrependRange<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasPrependRange<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasPrependRange<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasPrependRange<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasPrependRange<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasPrependRange<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
template <class Container, class Range>
concept HasAssignRange = requires (Container& c, Range&& range) {
c.assign_range(range);
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_assign_range() {
// Input range with the same value type.
static_assert(HasAssignRange<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasAssignRange<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasAssignRange<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasAssignRange<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasAssignRange<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasAssignRange<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
// Empty container.
template <class T>
TestCase<T> constexpr EmptyContainer_EmptyRange {
.initial = {}, .index = 0, .input = {}, .expected = {}
};
// Note: specializations for `bool` still use `vector<int>` for inputs. This is to avoid dealing with `vector<bool>` and
// its iterators over proxy types.
template <> constexpr TestCase<int> EmptyContainer_EmptyRange<bool> {
.initial = {}, .index = 0, .input = {}, .expected = {}
};
template <class T> constexpr TestCase<T> EmptyContainer_OneElementRange;
template <> constexpr TestCase<int> EmptyContainer_OneElementRange<int> {
.initial = {}, .index = 0, .input = {5}, .expected = {5}
};
template <> constexpr TestCase<char> EmptyContainer_OneElementRange<char> {
.initial = {}, .index = 0, .input = "a", .expected = "a"
};
template <> constexpr TestCase<int> EmptyContainer_OneElementRange<bool> {
.initial = {}, .index = 0, .input = {true}, .expected = {true}
};
template <class T> constexpr TestCase<T> EmptyContainer_MidRange;
template <> constexpr TestCase<int> EmptyContainer_MidRange<int> {
.initial = {}, .index = 0, .input = {5, 3, 1, 7, 9}, .expected = {5, 3, 1, 7, 9}
};
template <> constexpr TestCase<char> EmptyContainer_MidRange<char> {
.initial = {}, .index = 0, .input = "aeiou", .expected = "aeiou"
};
template <> constexpr TestCase<int> EmptyContainer_MidRange<bool> {
.initial = {}, .index = 0, .input = {1, 1, 0, 1, 1}, .expected = {1, 1, 0, 1, 1}
};
// One-element container.
template <class T> constexpr TestCase<T> OneElementContainer_Begin_EmptyRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_EmptyRange<int> {
.initial = {3}, .index = 0, .input = {}, .expected = {3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_EmptyRange<char> {
.initial = "B", .index = 0, .input = {}, .expected = "B"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_EmptyRange<bool> {
.initial = {0}, .index = 0, .input = {}, .expected = {0}
};
template <class T> constexpr TestCase<T> OneElementContainer_End_EmptyRange;
template <> constexpr TestCase<int> OneElementContainer_End_EmptyRange<int> {
.initial = {3}, .index = 1, .input = {}, .expected = {3}
};
template <> constexpr TestCase<char> OneElementContainer_End_EmptyRange<char> {
.initial = "B", .index = 1, .input = {}, .expected = "B"
};
template <> constexpr TestCase<int> OneElementContainer_End_EmptyRange<bool> {
.initial = {0}, .index = 1, .input = {}, .expected = {0}
};
template <class T> constexpr TestCase<T> OneElementContainer_Begin_OneElementRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_OneElementRange<int> {
.initial = {3}, .index = 0, .input = {-5}, .expected = {-5, 3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_OneElementRange<char> {
.initial = "B", .index = 0, .input = "a", .expected = "aB"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_OneElementRange<bool> {
.initial = {0}, .index = 0, .input = {1}, .expected = {1, 0}
};
template <class T> constexpr TestCase<T> OneElementContainer_End_OneElementRange;
template <> constexpr TestCase<int> OneElementContainer_End_OneElementRange<int> {
.initial = {3}, .index = 1, .input = {-5}, .expected = {3, -5}
};
template <> constexpr TestCase<char> OneElementContainer_End_OneElementRange<char> {
.initial = "B", .index = 1, .input = "a", .expected = "Ba"
};
template <> constexpr TestCase<int> OneElementContainer_End_OneElementRange<bool> {
.initial = {0}, .index = 1, .input = {1}, .expected = {0, 1}
};
template <class T> constexpr TestCase<T> OneElementContainer_Begin_MidRange;
template <> constexpr TestCase<int> OneElementContainer_Begin_MidRange<int> {
.initial = {3}, .index = 0, .input = {-5, -3, -1, -7, -9}, .expected = {-5, -3, -1, -7, -9, 3}
};
template <> constexpr TestCase<char> OneElementContainer_Begin_MidRange<char> {
.initial = "B", .index = 0, .input = "aeiou", .expected = "aeiouB"
};
template <> constexpr TestCase<int> OneElementContainer_Begin_MidRange<bool> {
.initial = {0}, .index = 0, .input = {1, 1, 0, 1, 1}, .expected = {1, 1, 0, 1, 1, 0}
};
template <class T> constexpr TestCase<T> OneElementContainer_End_MidRange;
template <> constexpr TestCase<int> OneElementContainer_End_MidRange<int> {
.initial = {3}, .index = 1, .input = {-5, -3, -1, -7, -9}, .expected = {3, -5, -3, -1, -7, -9}
};
template <> constexpr TestCase<char> OneElementContainer_End_MidRange<char> {
.initial = "B", .index = 1, .input = "aeiou", .expected = "Baeiou"
};
template <> constexpr TestCase<int> OneElementContainer_End_MidRange<bool> {
.initial = {0}, .index = 1, .input = {1, 1, 0, 1, 1}, .expected = {0, 1, 1, 0, 1, 1}
};
// Full container / empty range.
template <class T> constexpr TestCase<T> FullContainer_Begin_EmptyRange;
template <> constexpr TestCase<int> FullContainer_Begin_EmptyRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 0, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_EmptyRange<char> {
.initial = "_BCD_", .index = 0, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_EmptyRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 0, .input = {}, .expected = {0, 0, 1, 0, 0}
};
template <class T> constexpr TestCase<T> FullContainer_Mid_EmptyRange;
template <> constexpr TestCase<int> FullContainer_Mid_EmptyRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 2, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_EmptyRange<char> {
.initial = "_BCD_", .index = 2, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_EmptyRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 2, .input = {}, .expected = {0, 0, 1, 0, 0}
};
template <class T> constexpr TestCase<T> FullContainer_End_EmptyRange;
template <> constexpr TestCase<int> FullContainer_End_EmptyRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 5, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_End_EmptyRange<char> {
.initial = "_BCD_", .index = 5, .input = {}, .expected = "_BCD_"
};
template <> constexpr TestCase<int> FullContainer_End_EmptyRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 5, .input = {}, .expected = {0, 0, 1, 0, 0}
};
// Full container / one-element range.
template <class T> constexpr TestCase<T> FullContainer_Begin_OneElementRange;
template <> constexpr TestCase<int> FullContainer_Begin_OneElementRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 0, .input = {-5}, .expected = {-5, 11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_OneElementRange<char> {
.initial = "_BCD_", .index = 0, .input = "a", .expected = "a_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_OneElementRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 0, .input = {1}, .expected = {1, 0, 0, 1, 0, 0}
};
template <class T> constexpr TestCase<T> FullContainer_Mid_OneElementRange;
template <> constexpr TestCase<int> FullContainer_Mid_OneElementRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 2, .input = {-5}, .expected = {11, 29, -5, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_OneElementRange<char> {
.initial = "_BCD_", .index = 2, .input = "a", .expected = "_BaCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_OneElementRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 2, .input = {1}, .expected = {0, 0, 1, 1, 0, 0}
};
template <class T> constexpr TestCase<T> FullContainer_End_OneElementRange;
template <> constexpr TestCase<int> FullContainer_End_OneElementRange<int> {
.initial = {11, 29, 35, 14, 84}, .index = 5, .input = {-5}, .expected = {11, 29, 35, 14, 84, -5}
};
template <> constexpr TestCase<char> FullContainer_End_OneElementRange<char> {
.initial = "_BCD_", .index = 5, .input = "a", .expected = "_BCD_a"
};
template <> constexpr TestCase<int> FullContainer_End_OneElementRange<bool> {
.initial = {0, 0, 1, 0, 0}, .index = 5, .input = {1}, .expected = {0, 0, 1, 0, 0, 1}
};
// Full container / mid-sized range.
template <class T> constexpr TestCase<T> FullContainer_Begin_MidRange;
template <> constexpr TestCase<int> FullContainer_Begin_MidRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 0,
.input = {-5, -3, -1, -7, -9},
.expected = {-5, -3, -1, -7, -9, 11, 29, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Begin_MidRange<char> {
.initial = "_BCD_",
.index = 0,
.input = "aeiou",
.expected = "aeiou_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_MidRange<bool> {
.initial = {0, 0, 1, 0, 1},
.index = 0,
.input = {1, 1, 0, 1, 1},
.expected = {1, 1, 0, 1, 1, 0, 0, 1, 0, 1}
};
template <class T> constexpr TestCase<T> FullContainer_Mid_MidRange;
template <> constexpr TestCase<int> FullContainer_Mid_MidRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 2,
.input = {-5, -3, -1, -7, -9},
.expected = {11, 29, -5, -3, -1, -7, -9, 35, 14, 84}
};
template <> constexpr TestCase<char> FullContainer_Mid_MidRange<char> {
.initial = "_BCD_",
.index = 2,
.input = "aeiou",
.expected = "_BaeiouCD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_MidRange<bool> {
.initial = {0, 0, 1, 0, 1},
.index = 2,
.input = {1, 1, 0, 1, 1},
.expected = {0, 0, 1, 1, 0, 1, 1, 1, 0, 1}
};
template <class T> constexpr TestCase<T> FullContainer_End_MidRange;
template <> constexpr TestCase<int> FullContainer_End_MidRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 5,
.input = {-5, -3, -1, -7, -9},
.expected = {11, 29, 35, 14, 84, -5, -3, -1, -7, -9}
};
template <> constexpr TestCase<char> FullContainer_End_MidRange<char> {
.initial = "_BCD_",
.index = 5,
.input = "aeiou",
.expected = "_BCD_aeiou"
};
template <> constexpr TestCase<int> FullContainer_End_MidRange<bool> {
.initial = {0, 0, 1, 0, 1},
.index = 5,
.input = {1, 1, 0, 1, 1},
.expected = {0, 0, 1, 0, 1, 1, 1, 0, 1, 1}
};
// Full container / long range.
template <class T> constexpr TestCase<T> FullContainer_Begin_LongRange;
template <> constexpr TestCase<int> FullContainer_Begin_LongRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 0,
.input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
.expected = {
-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48, 11, 29, 35, 14, 84
}
};
template <> constexpr TestCase<char> FullContainer_Begin_LongRange<char> {
.initial = "_BCD_",
.index = 0,
.input = "aeiouqwxyz5781964203",
.expected = "aeiouqwxyz5781964203_BCD_"
};
template <> constexpr TestCase<int> FullContainer_Begin_LongRange<bool> {
.initial = {0, 0, 1, 0, 0},
.index = 0,
.input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
.expected = {
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0
}
};
template <class T> constexpr TestCase<T> FullContainer_Mid_LongRange;
template <> constexpr TestCase<int> FullContainer_Mid_LongRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 2,
.input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
.expected = {
11, 29, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48, 35, 14, 84
}
};
template <> constexpr TestCase<char> FullContainer_Mid_LongRange<char> {
.initial = "_BCD_",
.index = 2,
.input = "aeiouqwxyz5781964203",
.expected = "_Baeiouqwxyz5781964203CD_"
};
template <> constexpr TestCase<int> FullContainer_Mid_LongRange<bool> {
.initial = {0, 0, 1, 0, 0},
.index = 2,
.input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
.expected = {
0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0
}
};
template <class T> constexpr TestCase<T> FullContainer_End_LongRange;
template <> constexpr TestCase<int> FullContainer_End_LongRange<int> {
.initial = {11, 29, 35, 14, 84},
.index = 5,
.input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
.expected = {
11, 29, 35, 14, 84, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48
}
};
template <> constexpr TestCase<char> FullContainer_End_LongRange<char> {
.initial = "_BCD_",
.index = 5,
.input = "aeiouqwxyz5781964203",
.expected = "_BCD_aeiouqwxyz5781964203"
};
template <> constexpr TestCase<int> FullContainer_End_LongRange<bool> {
.initial = {0, 0, 1, 0, 1},
.index = 5,
.input = {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
.expected = {
0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
}
};
// Sequence containers tests.
template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_insert_range(Validate validate) {
using T = typename Container::value_type;
using D = typename Container::difference_type;
auto get_pos = [](auto& c, auto& test_case) { return std::ranges::next(c.begin(), static_cast<D>(test_case.index)); };
auto test = [&](auto& test_case) {
Container c(test_case.initial.begin(), test_case.initial.end());
auto in = wrap_input<Iter, Sent>(test_case.input);
auto pos = get_pos(c, test_case);
auto result = c.insert_range(pos, in);
assert(result == get_pos(c, test_case));
validate(c);
return std::ranges::equal(c, test_case.expected);
};
{ // Empty container.
// empty_c.insert_range(end, empty_range)
assert(test(EmptyContainer_EmptyRange<T>));
// empty_c.insert_range(end, one_element_range)
assert(test(EmptyContainer_OneElementRange<T>));
// empty_c.insert_range(end, mid_range)
assert(test(EmptyContainer_MidRange<T>));
}
{ // One-element container.
// one_element_c.insert_range(begin, empty_range)
assert(test(OneElementContainer_Begin_EmptyRange<T>));
// one_element_c.insert_range(end, empty_range)
assert(test(OneElementContainer_End_EmptyRange<T>));
// one_element_c.insert_range(begin, one_element_range)
assert(test(OneElementContainer_Begin_OneElementRange<T>));
// one_element_c.insert_range(end, one_element_range)
assert(test(OneElementContainer_End_OneElementRange<T>));
// one_element_c.insert_range(begin, mid_range)
assert(test(OneElementContainer_Begin_MidRange<T>));
// one_element_c.insert_range(end, mid_range)
assert(test(OneElementContainer_End_MidRange<T>));
}
{ // Full container.
// full_container.insert_range(begin, empty_range)
assert(test(FullContainer_Begin_EmptyRange<T>));
// full_container.insert_range(mid, empty_range)
assert(test(FullContainer_Mid_EmptyRange<T>));
// full_container.insert_range(end, empty_range)
assert(test(FullContainer_End_EmptyRange<T>));
// full_container.insert_range(begin, one_element_range)
assert(test(FullContainer_Begin_OneElementRange<T>));
// full_container.insert_range(end, one_element_range)
assert(test(FullContainer_Mid_OneElementRange<T>));
// full_container.insert_range(end, one_element_range)
assert(test(FullContainer_End_OneElementRange<T>));
// full_container.insert_range(begin, mid_range)
assert(test(FullContainer_Begin_MidRange<T>));
// full_container.insert_range(mid, mid_range)
assert(test(FullContainer_Mid_MidRange<T>));
// full_container.insert_range(end, mid_range)
assert(test(FullContainer_End_MidRange<T>));
// full_container.insert_range(begin, long_range)
assert(test(FullContainer_Begin_LongRange<T>));
// full_container.insert_range(mid, long_range)
assert(test(FullContainer_Mid_LongRange<T>));
// full_container.insert_range(end, long_range)
assert(test(FullContainer_End_LongRange<T>));
}
}
template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_prepend_range(Validate validate) {
using T = typename Container::value_type;
auto test = [&](auto& test_case) {
Container c(test_case.initial.begin(), test_case.initial.end());
auto in = wrap_input<Iter, Sent>(test_case.input);
c.prepend_range(in);
validate(c);
return std::ranges::equal(c, test_case.expected);
};
{ // Empty container.
// empty_c.prepend_range(empty_range)
assert(test(EmptyContainer_EmptyRange<T>));
// empty_c.prepend_range(one_element_range)
assert(test(EmptyContainer_OneElementRange<T>));
// empty_c.prepend_range(mid_range)
assert(test(EmptyContainer_MidRange<T>));
}
{ // One-element container.
// one_element_c.prepend_range(empty_range)
assert(test(OneElementContainer_Begin_EmptyRange<T>));
// one_element_c.prepend_range(one_element_range)
assert(test(OneElementContainer_Begin_OneElementRange<T>));
// one_element_c.prepend_range(mid_range)
assert(test(OneElementContainer_Begin_MidRange<T>));
}
{ // Full container.
// full_container.prepend_range(empty_range)
assert(test(FullContainer_Begin_EmptyRange<T>));
// full_container.prepend_range(one_element_range)
assert(test(FullContainer_Begin_OneElementRange<T>));
// full_container.prepend_range(mid_range)
assert(test(FullContainer_Begin_MidRange<T>));
// full_container.prepend_range(long_range)
assert(test(FullContainer_Begin_LongRange<T>));
}
}
template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_append_range(Validate validate) {
using T = typename Container::value_type;
auto test = [&](auto& test_case) {
Container c(test_case.initial.begin(), test_case.initial.end());
auto in = wrap_input<Iter, Sent>(test_case.input);
c.append_range(in);
validate(c);
return std::ranges::equal(c, test_case.expected);
};
{ // Empty container.
// empty_c.append_range(empty_range)
assert(test(EmptyContainer_EmptyRange<T>));
// empty_c.append_range(one_element_range)
assert(test(EmptyContainer_OneElementRange<T>));
// empty_c.append_range(mid_range)
assert(test(EmptyContainer_MidRange<T>));
}
{ // One-element container.
// one_element_c.append_range(empty_range)
assert(test(OneElementContainer_End_EmptyRange<T>));
// one_element_c.append_range(one_element_range)
assert(test(OneElementContainer_End_OneElementRange<T>));
// one_element_c.append_range(mid_range)
assert(test(OneElementContainer_End_MidRange<T>));
}
{ // Full container.
// full_container.append_range(empty_range)
assert(test(FullContainer_End_EmptyRange<T>));
// full_container.append_range(one_element_range)
assert(test(FullContainer_End_OneElementRange<T>));
// full_container.append_range(mid_range)
assert(test(FullContainer_End_MidRange<T>));
// full_container.append_range(long_range)
assert(test(FullContainer_End_LongRange<T>));
}
}
template <class Container, class Iter, class Sent, class Validate>
constexpr void test_sequence_assign_range(Validate validate) {
using T = typename Container::value_type;
auto& initial_empty = EmptyContainer_EmptyRange<T>.initial;
auto& initial_one_element = OneElementContainer_Begin_EmptyRange<T>.initial;
auto& initial_full = FullContainer_Begin_EmptyRange<T>.initial;
auto& input_empty = FullContainer_Begin_EmptyRange<T>.input;
auto& input_one_element = FullContainer_Begin_OneElementRange<T>.input;
auto& input_mid_range = FullContainer_Begin_MidRange<T>.input;
auto& input_long_range = FullContainer_Begin_LongRange<T>.input;
auto test = [&](auto& initial, auto& input) {
Container c(initial.begin(), initial.end());
auto in = wrap_input<Iter, Sent>(input);
c.assign_range(in);
validate(c);
return std::ranges::equal(c, input);
};
{ // Empty container.
// empty_container.assign_range(empty_range)
assert(test(initial_empty, input_empty));
// empty_container.assign_range(one_element_range)
assert(test(initial_empty, input_one_element));
// empty_container.assign_range(mid_range)
assert(test(initial_empty, input_mid_range));
// empty_container.assign_range(long_range)
assert(test(initial_empty, input_long_range));
}
{ // One-element container.
// one_element_container.assign_range(empty_range)
assert(test(initial_one_element, input_empty));
// one_element_container.assign_range(one_element_range)
assert(test(initial_one_element, input_one_element));
// one_element_container.assign_range(mid_range)
assert(test(initial_one_element, input_mid_range));
// one_element_container.assign_range(long_range)
assert(test(initial_one_element, input_long_range));
}
{ // Full container.
// full_container.assign_range(empty_range)
assert(test(initial_full, input_empty));
// full_container.assign_range(one_element_range)
assert(test(initial_full, input_one_element));
// full_container.assign_range(mid_range)
assert(test(initial_full, input_mid_range));
// full_container.assign_range(long_range)
assert(test(initial_full, input_long_range));
}
}
// Move-only types.
template <template <class ...> class Container>
constexpr void test_sequence_insert_range_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
Container<MoveOnly> c;
c.insert_range(c.end(), in);
}
template <template <class ...> class Container>
constexpr void test_sequence_prepend_range_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
Container<MoveOnly> c;
c.prepend_range(in);
}
template <template <class ...> class Container>
constexpr void test_sequence_append_range_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
Container<MoveOnly> c;
c.append_range(in);
}
template <template <class ...> class Container>
constexpr void test_sequence_assign_range_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
Container<MoveOnly> c;
c.assign_range(in);
}
// Exception safety.
template <template <class ...> class Container>
void test_insert_range_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) {
Container<T> c;
c.insert_range(c.end(), std::ranges::subrange(from, to));
});
#endif
}
template <template <class ...> class Container, class T>
void test_insert_range_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(alloc);
c.insert_range(c.end(), in);
assert(false); // The function call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
template <template <class ...> class Container>
void test_prepend_range_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) {
Container<T> c;
c.prepend_range(std::ranges::subrange(from, to));
});
#endif
}
template <template <class ...> class Container, class T>
void test_prepend_range_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(alloc);
c.prepend_range(in);
assert(false); // The function call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
template <template <class ...> class Container>
void test_append_range_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) {
Container<T> c;
c.append_range(std::ranges::subrange(from, to));
});
#endif
}
template <template <class ...> class Container, class T>
void test_append_range_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(alloc);
c.append_range(in);
assert(false); // The function call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
template <template <class ...> class Container>
void test_assign_range_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) {
Container<T> c;
c.assign_range(std::ranges::subrange(from, to));
});
#endif
}
template <template <class ...> class Container, class T>
void test_assign_range_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(alloc);
c.assign_range(in);
assert(false); // The function call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
#endif // SUPPORT_INSERT_RANGE_SEQUENCE_CONTAINERS_H