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