Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
strong_vector.h
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14// This file provides the StrongVector container that wraps around the STL
15// vector. The wrapper restrict indexing to a pre-specified type-safe integer
16// type or StrongInt (see util/intops/strong_int.h). It prevents accidental
17// indexing by different "logical" integer-like types (e.g. another StrongInt)
18// or native integer types. The wrapper is useful as C++ and the standard
19// template library allows the user to mix "logical" integral indices that might
20// have a different role.
21//
22// The container can only be indexed by an instance of an StrongInt class, which
23// can be declared as:
24//
25// DEFINE_STRONG_INT_TYPE(type_name, value_type);
26//
27// where type_name is the desired name for the "logical" integer-like type
28// and the value_type is a supported native integer type such as int or
29// uint64_t (see util/intops/strong_int.h for details).
30//
31// The wrapper exposes all public methods of STL vector and behaves mostly as
32// pass-through. The only methods modified to ensure type-safety are the
33// operator [] and the at() methods.
34//
35// EXAMPLES --------------------------------------------------------------------
36//
37// DEFINE_STRONG_INT_TYPE(PhysicalChildIndex, int32_t);
38// StrongVector<PhysicalChildIndex, ChildStats*> vec;
39//
40// PhysicalChildIndex physical_index;
41// vec[physical_index] = ...; <-- index type match: compiles properly.
42// vec.at(physical_index) = ...; <-- index type match: compiles properly.
43//
44// int32_t physical_index;
45// vec[physical_index] = ...; <-- fails to compile.
46// vec.at(physical_index) = ...; <-- fails to compile.
47//
48// DEFINE_STRONG_INT_TYPE(LogicalChildIndex, int32_t);
49// LogicalChildIndex logical_index;
50// vec[logical_index] = ...; <-- fails to compile.
51// vec.at(logical_index) = ...; <-- fails to compile.
52//
53// NB: Iterator arithmetic bypasses strong typing for the index.
54//
55// OVERFLOW BEHAVIOR
56//
57// This class ONLY guards against growing the size beyond the range
58// indexable by the index type in debug mode. In optimized mode the
59// user can CHECK IsValidSize() when deemed important.
60
61#ifndef OR_TOOLS_BASE_STRONG_VECTOR_H_
62#define OR_TOOLS_BASE_STRONG_VECTOR_H_
63
64#include <limits>
65#include <type_traits>
66#include <vector>
67
70
71namespace util_intops {
72
73template <typename IntType, typename NativeType,
74 typename Alloc = std::allocator<NativeType>>
75class StrongVector : protected std::vector<NativeType, Alloc> {
76 public:
77 typedef std::vector<NativeType, Alloc> ParentType;
78 typedef typename ParentType::size_type size_type;
79 typedef typename ParentType::allocator_type allocator_type;
80 typedef typename ParentType::value_type value_type;
81 typedef typename ParentType::reference reference;
82 typedef typename ParentType::const_reference const_reference;
83 typedef typename ParentType::pointer pointer;
84 typedef typename ParentType::const_pointer const_pointer;
85 typedef typename ParentType::iterator iterator;
86 typedef typename ParentType::const_iterator const_iterator;
87 typedef typename ParentType::reverse_iterator reverse_iterator;
88 typedef typename ParentType::const_reverse_iterator const_reverse_iterator;
89
90 public:
92 explicit StrongVector(const allocator_type& a) : ParentType(a) {}
93 explicit StrongVector(size_type n) : ParentType(n) { DCHECK(IsValidSize()); }
94 explicit StrongVector(IntType n)
95 : StrongVector(static_cast<size_type>(n.value())) {}
96 explicit StrongVector(size_type n, const value_type& v,
98 : ParentType(n, v, a) {
99 DCHECK(IsValidSize());
100 }
101 explicit StrongVector(IntType n, const value_type& v,
103 : StrongVector(static_cast<size_type>(n.value()), v, a) {}
105 DCHECK(IsValidSize());
106 }
108 StrongVector(std::initializer_list<value_type> l,
110 : ParentType(l, a) {
111 DCHECK(IsValidSize());
112 }
113 template <typename InputIteratorType>
114 StrongVector(InputIteratorType first, InputIteratorType last,
116 : ParentType(first, last, a) {
117 DCHECK(IsValidSize());
118 }
120
121 // -- Accessors --------------------------------------------------------------
122 // This const accessor is useful in defining the comparison operators below.
123 const ParentType& get() const { return *this; }
124 // The mutable accessor is useful when using auxiliary methods relying on
125 // vector parameters such as JoinUsing(), SplitStringUsing(), etc. Methods
126 // relying solely on iterators (e.g. STLDeleteElements) should work just fine
127 // without the need for mutable_get(). NB: It should be used only in this
128 // case and thus should not be abused to index the underlying vector without
129 // the appropriate IntType.
130 ParentType* mutable_get() { return this; }
131
132 // -- Modified methods -------------------------------------------------------
134 return ParentType::operator[](static_cast<size_type>(i.value()));
135 }
136 const_reference operator[](IntType i) const {
137 return ParentType::operator[](static_cast<size_type>(i.value()));
138 }
139 reference at(IntType i) {
140 return ParentType::at(static_cast<size_type>(i.value()));
141 }
142 const_reference at(IntType i) const {
143 return ParentType::at(static_cast<size_type>(i.value()));
144 }
145
146 // -- Extension methods ------------------------------------------------------
147
148 // Iteration related methods. Useful for parallel iteration and
149 // non-trivial access patterns. Typical loop will be:
150 // for (auto i = v.start_index(); i < v.end_index(); ++i) ...
151 IntType start_index() const { return IntType(0); }
152 // Index following the last valid index into the vector. In case
153 // size() has grown beyond values representable by IntType, this
154 // function will truncate the result. There is a debugging check for
155 // such behavior, but it is unlikely to be triggered in testing.
156 IntType end_index() const {
157 DCHECK(IsValidSize());
158 return IntType(size());
159 }
160
161 // Returns true if the vector is fully addressable by the index type.
162 bool IsValidSize() const { return ValidSize(size()); }
163
164 // Most methods from vector can be reused without any changes.
165 using ParentType::back;
166 using ParentType::begin;
167 using ParentType::capacity;
168 using ParentType::cbegin;
169 using ParentType::cend;
170 using ParentType::clear;
171 using ParentType::empty;
172 using ParentType::end;
173 using ParentType::erase;
174 using ParentType::front;
175 using ParentType::max_size;
176 using ParentType::pop_back;
177 using ParentType::rbegin;
178 using ParentType::rend;
179 using ParentType::shrink_to_fit;
180
181 // Returns an iterator of valid indices into this vector. Goes from
182 // start_index() to end_index(). This is useful for cases of
183 // parallel iteration over several vectors indexed by the same type, e.g.
184 // StrongVector<MyInt, foo> v1;
185 // StrongVector<MyInt, foo> v2;
186 // CHECK_EQ(v1.size(), v2.size());
187 // for (const auto i : v1.index_range()) {
188 // do_stuff(v1[i], v2[i]);
189 // }
193
194 // -- Pass-through methods to STL vector -------------------------------------
195
196 // Note that vector<bool>::data() does not exist. By wrapping data()
197 // below, this allows StrongVector<T, bool> to still compile, as long as
198 // StrongVector<T, bool>::data() is never called.
199 value_type* data() { return ParentType::data(); }
200 const value_type* data() const { return ParentType::data(); }
201
203 ParentType::operator=(x.get());
204 return *this;
205 }
207 StrongVector& operator=(std::initializer_list<value_type> l) {
208 ParentType::operator=(l);
209 DCHECK(IsValidSize());
210 return *this;
211 }
212
213 void swap(StrongVector& x) noexcept { ParentType::swap(*x.mutable_get()); }
214
215 void assign(size_type n, const value_type& val) {
216 DCHECK(ValidSize(n));
217 ParentType::assign(n, val);
218 }
219 template <typename InputIt>
220 void assign(InputIt f, InputIt l) {
221 ParentType::assign(f, l);
222 DCHECK(IsValidSize());
223 }
224 void assign(std::initializer_list<value_type> l) {
225 ParentType::assign(l);
226 DCHECK(IsValidSize());
227 }
228
229 template <typename... Args>
230 iterator emplace(const_iterator pos, Args&&... args) {
231 iterator result = ParentType::emplace(pos, std::forward<Args>(args)...);
232 DCHECK(IsValidSize());
233 return result;
234 }
235
236 template <typename... Args>
237 reference emplace_back(Args&&... args) {
238 reference value = ParentType::emplace_back(std::forward<Args>(args)...);
239 DCHECK(IsValidSize());
240 return value;
241 }
242
244 iterator result = ParentType::insert(pos, x);
245 DCHECK(IsValidSize());
246 return result;
247 }
249 iterator result = ParentType::insert(pos, std::move(x));
250 DCHECK(IsValidSize());
251 return result;
252 }
254 ParentType::insert(pos, n, x);
255 DCHECK(IsValidSize());
256 }
257 template <typename SIT>
258 void insert(const_iterator pos, SIT first, SIT last) {
259 ParentType::insert(pos, first, last);
260 DCHECK(IsValidSize());
261 }
262
263 void push_back(const value_type& val) {
264 ParentType::push_back(val);
265 DCHECK(IsValidSize());
266 }
267 void push_back(value_type&& val) {
268 ParentType::push_back(std::move(val));
269 DCHECK(IsValidSize());
270 }
271
273 DCHECK(ValidSize(n));
274 ParentType::reserve(n);
275 }
276
277 void reserve(IntType n) { reserve(static_cast<size_type>(n.value())); }
278
279 void resize(size_type new_size) {
280 DCHECK(ValidSize(new_size));
281 ParentType::resize(new_size);
282 }
283
284 void resize(IntType new_size) {
285 resize(static_cast<size_type>(new_size.value()));
286 }
287
288 void resize(size_type new_size, const value_type& x) {
289 DCHECK(ValidSize(new_size));
290 ParentType::resize(new_size, x);
291 }
292
293 void resize(IntType new_size, const value_type& x) {
294 resize(static_cast<size_type>(new_size.value()), x);
295 }
296
297 using ParentType::size;
298
299 static_assert(std::is_integral<typename IntType::ValueType>::value,
300 "int type indexed vector must have integral index");
301
302 template <typename H>
303 friend H AbslHashValue(H h, const StrongVector& v) {
304 return H::combine(std::move(h), v.get());
305 }
306
307 private:
308 // Checks that the given value n is in range of the index type.
309 static bool ValidSize(size_type n) {
310 return n <= std::numeric_limits<typename IntType::ValueType>::max();
311 }
312
313 friend bool operator==(const StrongVector& x, const StrongVector& y) {
314 return x.get() == y.get();
315 }
316 friend bool operator!=(const StrongVector& x, const StrongVector& y) {
317 return x.get() != y.get();
318 }
319 friend bool operator<(const StrongVector& x, const StrongVector& y) {
320 return x.get() < y.get();
321 }
322 friend bool operator>(const StrongVector& x, const StrongVector& y) {
323 return x.get() > y.get();
324 }
325 friend bool operator<=(const StrongVector& x, const StrongVector& y) {
326 return x.get() <= y.get();
327 }
328 friend bool operator>=(const StrongVector& x, const StrongVector& y) {
329 return x.get() >= y.get();
330 }
331 friend void swap(StrongVector& x, StrongVector& y) noexcept { x.swap(y); }
332};
333
334} // namespace util_intops
335
336#endif // OR_TOOLS_BASE_STRONG_VECTOR_H_
IntegerValue y
IntegerValue size
ParentType::reference reference
friend H AbslHashValue(H h, const StrongVector &v)
friend bool operator!=(const StrongVector &x, const StrongVector &y)
ParentType::const_iterator const_iterator
friend void swap(StrongVector &x, StrongVector &y) noexcept
void assign(size_type n, const value_type &val)
ParentType::const_reference const_reference
value_type * data()
– Pass-through methods to STL vector ----------------------------------—
StrongVector(InputIteratorType first, InputIteratorType last, const allocator_type &a=allocator_type())
StrongVector(size_type n, const value_type &v, const allocator_type &a=allocator_type())
const ParentType & get() const
void swap(StrongVector &x) noexcept
void assign(InputIt f, InputIt l)
StrongVector & operator=(StrongVector &&x)=default
void resize(IntType new_size)
void resize(IntType new_size, const value_type &x)
void push_back(const value_type &val)
friend bool operator>=(const StrongVector &x, const StrongVector &y)
iterator insert(const_iterator pos, value_type &&x)
friend bool operator==(const StrongVector &x, const StrongVector &y)
StrongVector & operator=(std::initializer_list< value_type > l)
ParentType::const_pointer const_pointer
iterator insert(const_iterator pos, const value_type &x)
StrongVector & operator=(const StrongVector &x)
ParentType::value_type value_type
const value_type * data() const
friend bool operator>(const StrongVector &x, const StrongVector &y)
void reserve(size_type n)
ParentType::allocator_type allocator_type
IntType start_index() const
– Extension methods ---------------------------------------------------—
const_reference at(IntType i) const
reference emplace_back(Args &&... args)
ParentType::iterator iterator
StrongVector(std::initializer_list< value_type > l, const allocator_type &a=allocator_type())
const_reference operator[](IntType i) const
bool IsValidSize() const
Returns true if the vector is fully addressable by the index type.
ParentType::reverse_iterator reverse_iterator
std::vector< NativeType, Alloc > ParentType
StrongVector(const allocator_type &a)
ParentType::const_reverse_iterator const_reverse_iterator
void resize(size_type new_size)
void resize(size_type new_size, const value_type &x)
void assign(std::initializer_list< value_type > l)
StrongVector(StrongVector &&x)=default
StrongVector(IntType n, const value_type &v, const allocator_type &a=allocator_type())
friend bool operator<=(const StrongVector &x, const StrongVector &y)
ParentType::size_type size_type
reference operator[](IntType i)
– Modified methods ----------------------------------------------------—
iterator emplace(const_iterator pos, Args &&... args)
reference at(IntType i)
void insert(const_iterator pos, size_type n, const value_type &x)
ParentType::pointer pointer
StrongIntRange< IntType > index_range() const
void insert(const_iterator pos, SIT first, SIT last)
friend bool operator<(const StrongVector &x, const StrongVector &y)
void push_back(value_type &&val)
StrongVector(const StrongVector &x)
int64_t a
Definition table.cc:44
int64_t value
const Variable x
Definition qp_tests.cc:127