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 | #ifndef RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H |
10 | #define RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H |
11 | |
12 | #include <algorithm> |
13 | #include <concepts> |
14 | #include <cstddef> |
15 | |
16 | enum class CtrChoice { Invalid, DefaultCtrAndInsert, BeginEndPair, FromRangeT, DirectCtr }; |
17 | |
18 | enum class InserterChoice { Invalid, Insert, PushBack }; |
19 | |
20 | // Allows checking that `ranges::to` correctly follows the order of priority of different constructors -- e.g., if |
21 | // 3 constructors are available, the `from_range_t` constructor is chosen in favor of the constructor taking two |
22 | // iterators, etc. |
23 | template <class ElementType, CtrChoice Rank, InserterChoice Inserter = InserterChoice::Insert, bool CanReserve = false> |
24 | struct Container { |
25 | CtrChoice ctr_choice = CtrChoice::Invalid; |
26 | InserterChoice inserter_choice = InserterChoice::Invalid; |
27 | bool called_reserve = false; |
28 | |
29 | int = 0; |
30 | char = 0; |
31 | |
32 | using value_type = ElementType; |
33 | static constexpr int Capacity = 8; |
34 | int size_ = 0; |
35 | ElementType buffer_[Capacity] = {}; |
36 | |
37 | // Case 1 -- construct directly from the range. |
38 | |
39 | constexpr explicit Container(std::ranges::input_range auto&& in) |
40 | requires(Rank >= CtrChoice::DirectCtr) |
41 | : ctr_choice(CtrChoice::DirectCtr), size_(static_cast<int>(std::ranges::size(in))) { |
42 | std::ranges::copy(in, begin()); |
43 | } |
44 | |
45 | // Check that `ranges::to` can also pass extra parameters. |
46 | constexpr explicit Container(std::ranges::input_range auto&& in, int arg1, char arg2) |
47 | requires(Rank >= CtrChoice::DirectCtr) |
48 | : Container(in) { |
49 | extra_arg1 = arg1; |
50 | extra_arg2 = arg2; |
51 | } |
52 | |
53 | // Case 2 -- use `from_range_t` constructor. |
54 | |
55 | constexpr Container(std::from_range_t, std::ranges::input_range auto&& in) |
56 | requires(Rank >= CtrChoice::FromRangeT) |
57 | : ctr_choice(CtrChoice::FromRangeT), size_(static_cast<int>(std::ranges::size(in))) { |
58 | std::ranges::copy(in, begin()); |
59 | } |
60 | |
61 | constexpr Container(std::from_range_t, std::ranges::input_range auto&& in, int arg1, char arg2) |
62 | requires(Rank >= CtrChoice::FromRangeT) |
63 | : Container(std::from_range, in) { |
64 | extra_arg1 = arg1; |
65 | extra_arg2 = arg2; |
66 | } |
67 | |
68 | // Case 3 -- use begin-end pair. |
69 | |
70 | template <class Iter> |
71 | constexpr Container(Iter b, Iter e) |
72 | requires(Rank >= CtrChoice::BeginEndPair) |
73 | : ctr_choice(CtrChoice::BeginEndPair), size_(static_cast<int>(e - b)) { |
74 | std::ranges::copy(b, e, begin()); |
75 | } |
76 | |
77 | template <class Iter> |
78 | constexpr Container(Iter b, Iter e, int arg1, char arg2) |
79 | requires(Rank >= CtrChoice::BeginEndPair) |
80 | : Container(b, e) { |
81 | extra_arg1 = arg1; |
82 | extra_arg2 = arg2; |
83 | } |
84 | |
85 | // Case 4 -- default-construct and insert, reserving the size if possible. |
86 | |
87 | constexpr Container() |
88 | requires(Rank >= CtrChoice::DefaultCtrAndInsert) |
89 | : ctr_choice(CtrChoice::DefaultCtrAndInsert) {} |
90 | |
91 | constexpr Container(int arg1, char arg2) |
92 | requires(Rank >= CtrChoice::DefaultCtrAndInsert) |
93 | : ctr_choice(CtrChoice::DefaultCtrAndInsert), extra_arg1(arg1), extra_arg2(arg2) {} |
94 | |
95 | constexpr ElementType* begin() { return buffer_; } |
96 | constexpr ElementType* end() { return buffer_ + size_; } |
97 | constexpr std::size_t size() const { return size_; } |
98 | |
99 | template <class T> |
100 | constexpr void push_back(T val) |
101 | requires(Inserter >= InserterChoice::PushBack) |
102 | { |
103 | inserter_choice = InserterChoice::PushBack; |
104 | buffer_[size_] = val; |
105 | ++size_; |
106 | } |
107 | |
108 | template <class T> |
109 | constexpr ElementType* insert(ElementType* where, T val) |
110 | requires(Inserter >= InserterChoice::Insert) |
111 | { |
112 | assert(size() + 1 <= Capacity); |
113 | |
114 | inserter_choice = InserterChoice::Insert; |
115 | |
116 | std::shift_right(where, end(), 1); |
117 | *where = val; |
118 | ++size_; |
119 | |
120 | return where; |
121 | } |
122 | |
123 | constexpr void reserve(size_t) |
124 | requires CanReserve |
125 | { |
126 | called_reserve = true; |
127 | } |
128 | |
129 | constexpr std::size_t capacity() const |
130 | requires CanReserve |
131 | { |
132 | return Capacity; |
133 | } |
134 | |
135 | constexpr std::size_t max_size() const |
136 | requires CanReserve |
137 | { |
138 | return Capacity; |
139 | } |
140 | |
141 | friend constexpr bool operator==(const Container&, const Container&) = default; |
142 | }; |
143 | |
144 | #endif // RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H |
145 | |