Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
strong_int.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// StrongInt is a simple template class mechanism for defining "logical"
15// integer-like class types that support many of the same functionalities
16// as native integer types, but which prevent assignment, construction, and
17// other operations from other similar integer-like types. Essentially, the
18// template class StrongInt<StrongIntName, ValueType> (where ValueType assumes
19// valid scalar types such as int, uint, int32_t, etc) has the additional
20// property that it cannot be assigned to or constructed from other StrongInts
21// or native integer types of equal or implicitly convertible type.
22//
23// The class is useful for preventing mingling of integer variables with
24// different logical roles or units. Unfortunately, C++ provides relatively
25// good type-safety for user-defined classes but not for integer types. It is
26// essentially up to the user to use nice variable names and comments to prevent
27// accidental mismatches, such as confusing a user-index with a group-index or a
28// time-in-milliseconds with a time-in-seconds. The use of typedefs are limited
29// in that regard as they do not enforce type-safety.
30//
31// USAGE -----------------------------------------------------------------------
32//
33// DEFINE_STRONG_INT_TYPE(StrongIntName, ValueType);
34//
35// where:
36// StrongIntName: is the desired (unique) name for the "logical" integer type
37// ValueType: is one of the integral types as defined by std::is_integral
38// (see <type_traits>).
39//
40// DISALLOWED OPERATIONS / TYPE-SAFETY ENFORCEMENT -----------------------------
41//
42// Consider these definitions and variable declarations:
43// DEFINE_STRONG_INT_TYPE(GlobalDocID, int64_t);
44// DEFINE_STRONG_INT_TYPE(LocalDocID, int64_t);
45// GlobalDocID global;
46// LocalDocID local;
47//
48// The class StrongInt prevents:
49//
50// 1) Assignments of other StrongInts with different StrongIntNames.
51//
52// global = local; <-- Fails to compile!
53// local = global; <-- Fails to compile!
54//
55// 2) Explicit/implicit conversion from an StrongInt to another StrongInt.
56//
57// LocalDocID l(global); <-- Fails to compile!
58// LocalDocID l = global; <-- Fails to compile!
59//
60// void GetGlobalDoc(GlobalDocID global) { }
61// GetGlobalDoc(global); <-- Compiles fine, types match!
62// GetGlobalDoc(local); <-- Fails to compile!
63//
64// 3) Implicit conversion from an StrongInt to a native integer type.
65//
66// void GetGlobalDoc(int64_t global) { ...
67// GetGlobalDoc(global); <-- Fails to compile!
68// GetGlobalDoc(local); <-- Fails to compile!
69//
70// void GetLocalDoc(int32_t local) { ...
71// GetLocalDoc(global); <-- Fails to compile!
72// GetLocalDoc(local); <-- Fails to compile!
73//
74//
75// SUPPORTED OPERATIONS --------------------------------------------------------
76//
77// The following operators are supported: unary: ++ (both prefix and postfix),
78// +, -, ! (logical not), ~ (one's complement); comparison: ==, !=, <, <=, >,
79// >=; numerical: +, -, *, /; assignment: =, +=, -=, /=, *=; stream: <<. Each
80// operator allows the same StrongIntName and the ValueType to be used on
81// both left- and right-hand sides.
82//
83// It also supports an accessor value() returning the stored value as ValueType,
84// and a templatized accessor value<T>() method that serves as syntactic sugar
85// for static_cast<T>(var.value()). These accessors are useful when assigning
86// the stored value into protocol buffer fields and using it as printf args.
87//
88// The class also defines a hash functor that allows the StrongInt to be used
89// as key to hashable containers such as hash_map and hash_set.
90//
91// We suggest using the StrongIntIndexedContainer wrapper around google3's
92// FixedArray and STL vector (see int-type-indexed-container.h) if an StrongInt
93// is intended to be used as an index into these containers. These wrappers are
94// indexed in a type-safe manner using StrongInts to ensure type-safety.
95//
96// NB: this implementation does not attempt to abide by or enforce dimensional
97// analysis on these scalar types.
98//
99// EXAMPLES --------------------------------------------------------------------
100//
101// DEFINE_STRONG_INT_TYPE(GlobalDocID, int64_t);
102// GlobalDocID global = 3;
103// std::cout << global; <-- Prints 3 to stdout.
104//
105// for (GlobalDocID i(0); i < global; ++i) {
106// std::cout << i;
107// } <-- Print(ln)s 0 1 2 to stdout
108//
109// DEFINE_STRONG_INT_TYPE(LocalDocID, int64_t);
110// LocalDocID local;
111// std::cout << local; <-- Prints 0 to stdout it
112// default
113// initializes the value to 0.
114//
115// local = 5;
116// local *= 2;
117// LocalDocID l(local);
118// std::cout << l + local; <-- Prints 20 to stdout.
119//
120// GenericSearchRequest request;
121// request.set_doc_id(global.value()); <-- Uses value() to extract the value
122// from the StrongInt class.
123//
124// REMARKS ---------------------------------------------------------------------
125//
126// The following bad usage is permissible although discouraged. Essentially, it
127// involves using the value*() accessors to extract the native integer type out
128// of the StrongInt class. Keep in mind that the primary reason for the
129// StrongInt class is to prevent *accidental* mingling of similar logical
130// integer types -- and not type casting from one type to another.
131//
132// DEFINE_STRONG_INT_TYPE(GlobalDocID, int64_t);
133// DEFINE_STRONG_INT_TYPE(LocalDocID, int64_t);
134// GlobalDocID global;
135// LocalDocID local;
136//
137// global = local.value(); <-- Compiles fine.
138//
139// void GetGlobalDoc(GlobalDocID global) { ...
140// GetGlobalDoc(local.value()); <-- Compiles fine.
141//
142// void GetGlobalDoc(int64_t global) { ...
143// GetGlobalDoc(local.value()); <-- Compiles fine.
144
145#ifndef OR_TOOLS_BASE_STRONG_INT_H_
146#define OR_TOOLS_BASE_STRONG_INT_H_
147
148#include <stddef.h>
149
150#include <functional>
151#include <iosfwd>
152#include <ostream> // NOLINT
153#include <type_traits>
154
155#include "absl/base/port.h"
156#include "absl/strings/str_format.h"
157#include "absl/strings/string_view.h"
158#include "ortools/base/macros.h"
159
160namespace util_intops {
161
162template <typename StrongIntName, typename _ValueType>
163class StrongInt;
164
165// Defines the StrongInt using value_type and typedefs it to int_type_name.
166// The struct int_type_name ## _tag_ trickery is needed to ensure that a new
167// type is created per int_type_name.
168#define DEFINE_STRONG_INT_TYPE(int_type_name, value_type) \
169 struct int_type_name##_tag_ { \
170 static constexpr absl::string_view TypeName() { return #int_type_name; } \
171 }; \
172 typedef ::util_intops::StrongInt<int_type_name##_tag_, value_type> \
173 int_type_name;
174
175// Holds a integral value (of type ValueType) and behaves as a
176// ValueType by exposing assignment, unary, comparison, and arithmetic
177// operators.
178//
179// The template parameter StrongIntName defines the name for the int type and
180// must be unique within a binary (the convenient DEFINE_STRONG_INT macro at the
181// end of the file generates a unique StrongIntName). The parameter ValueType
182// defines the integer type value (see supported list above).
183//
184// This class is NOT thread-safe.
185template <typename StrongIntName, typename _ValueType>
187 public:
188 typedef _ValueType ValueType; // for non-member operators
190
191 static constexpr absl::string_view TypeName() {
192 return StrongIntName::TypeName();
193 }
194
195 // Note that this may change from time to time without notice.
196 // See .
197 struct Hasher {
198 size_t operator()(const StrongInt& arg) const {
199 return static_cast<size_t>(arg.value());
200 }
201 };
202
203 public:
204 // Default c'tor initializing value_ to 0.
205 constexpr StrongInt() : value_(0) {}
206 // C'tor explicitly initializing from a ValueType.
207 constexpr explicit StrongInt(ValueType value) : value_(value) {}
208
209 // StrongInt uses the default copy constructor, destructor and assign
210 // operator. The defaults are sufficient and omitting them allows the compiler
211 // to add the move constructor/assignment.
212
213 // -- ACCESSORS --------------------------------------------------------------
214 // The class provides a value() accessor returning the stored ValueType value_
215 // as well as a templatized accessor that is just a syntactic sugar for
216 // static_cast<T>(var.value());
217 constexpr ValueType value() const { return value_; }
218
219 template <typename ValType>
220 constexpr ValType value() const {
221 return static_cast<ValType>(value_);
222 }
223
224 // Explicitly cast the raw value only if the underlying value is convertible
225 // to T.
226 template <typename T,
227 typename = std::enable_if_t<std::conjunction_v<
228 std::bool_constant<std::numeric_limits<T>::is_integer>,
229 std::is_convertible<ValueType, T>>>>
230 constexpr explicit operator T() const {
231 return static_cast<T>(value_);
232 }
233
234 // -- UNARY OPERATORS --------------------------------------------------------
235 ThisType& operator++() { // prefix ++
236 ++value_;
237 return *this;
238 }
239 const ThisType operator++(int v) { // postfix ++
240 ThisType temp(*this);
241 ++value_;
242 return temp;
243 }
244 ThisType& operator--() { // prefix --
245 --value_;
246 return *this;
247 }
248 const ThisType operator--(int v) { // postfix --
249 ThisType temp(*this);
250 --value_;
251 return temp;
252 }
253
254 constexpr bool operator!() const { return value_ == 0; }
255 constexpr const ThisType operator+() const { return ThisType(value_); }
256 constexpr const ThisType operator-() const { return ThisType(-value_); }
257 constexpr const ThisType operator~() const { return ThisType(~value_); }
258
259 // -- ASSIGNMENT OPERATORS ---------------------------------------------------
260 // We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
261 // and %= for both ThisType and ValueType.
262#define STRONG_INT_TYPE_ASSIGNMENT_OP(op) \
263 ThisType& operator op(const ThisType & arg_value) { \
264 value_ op arg_value.value(); \
265 return *this; \
266 } \
267 ThisType& operator op(ValueType arg_value) { \
268 value_ op arg_value; \
269 return *this; \
270 }
275 STRONG_INT_TYPE_ASSIGNMENT_OP(<<=); // NOLINT
278#undef STRONG_INT_TYPE_ASSIGNMENT_OP
279
281 value_ = arg_value;
282 return *this;
283 }
284
285 private:
286 // The integer value of type ValueType.
287 ValueType value_;
288
289 COMPILE_ASSERT(std::is_integral<ValueType>::value,
290 invalid_integer_type_for_id_type_);
292
293// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
294// We provide the << operator, primarily for logging purposes. Currently, there
295// seems to be no need for an >> operator.
296template <typename StrongIntName, typename ValueType>
297std::ostream& operator<<(std::ostream& os, // NOLINT
299 return os << arg.value();
300}
301
302// Define AbslStringify, for absl::StrAppend, absl::StrCat, and absl::StrFormat.
303//
304// When using StrongInt with absl::StrFormat, use the "%v" specifier.
305template <typename Sink, typename... T>
306void AbslStringify(Sink& sink, StrongInt<T...> arg) {
307 using ValueType = typename decltype(arg)::ValueType;
308 // int8_t/uint8_t are not supported by the "%v" specifier due to it being
309 // ambiguous whether an integer or character should be printed.
310 if constexpr (std::is_same_v<ValueType, int8_t>) {
311 absl::Format(&sink, "%d", arg.value());
312 } else if constexpr (std::is_same_v<ValueType, uint8_t>) {
313 absl::Format(&sink, "%u", arg.value());
314 } else {
315 absl::Format(&sink, "%v", arg.value());
316 }
317}
318
319// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
320// We support only the +, -, *, and / operators with the same StrongInt and
321// ValueType types. The reason is to allow simple manipulation on these IDs
322// when used as indices in vectors and arrays.
323//
324// NB: Although it is possible to do StrongInt * StrongInt and StrongInt /
325// StrongInt, it is probably non-sensical from a dimensionality analysis
326// perspective.
327#define STRONG_INT_TYPE_ARITHMETIC_OP(op) \
328 template <typename StrongIntName, typename ValueType> \
329 constexpr StrongInt<StrongIntName, ValueType> operator op( \
330 StrongInt<StrongIntName, ValueType> id_1, \
331 StrongInt<StrongIntName, ValueType> id_2) { \
332 return StrongInt<StrongIntName, ValueType>(id_1.value() op id_2.value()); \
333 } \
334 template <typename StrongIntName, typename ValueType> \
335 constexpr StrongInt<StrongIntName, ValueType> operator op( \
336 StrongInt<StrongIntName, ValueType> id, \
337 typename StrongInt<StrongIntName, ValueType>::ValueType arg_val) { \
338 return StrongInt<StrongIntName, ValueType>(id.value() op arg_val); \
339 } \
340 template <typename StrongIntName, typename ValueType> \
341 constexpr StrongInt<StrongIntName, ValueType> operator op( \
342 typename StrongInt<StrongIntName, ValueType>::ValueType arg_val, \
343 StrongInt<StrongIntName, ValueType> id) { \
344 return StrongInt<StrongIntName, ValueType>(arg_val op id.value()); \
345 }
353#undef STRONG_INT_TYPE_ARITHMETIC_OP
354
355// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
356// Static inline comparison operators. We allow all comparison operators among
357// the following types (OP \in [==, !=, <, <=, >, >=]:
358// StrongInt<StrongIntName, ValueType> OP StrongInt<StrongIntName, ValueType>
359// StrongInt<StrongIntName, ValueType> OP ValueType
360// ValueType OP StrongInt<StrongIntName, ValueType>
361#define STRONG_INT_TYPE_COMPARISON_OP(op) \
362 template <typename StrongIntName, typename ValueType> \
363 static inline constexpr bool operator op( \
364 StrongInt<StrongIntName, ValueType> id_1, \
365 StrongInt<StrongIntName, ValueType> id_2) { \
366 return id_1.value() op id_2.value(); \
367 } \
368 template <typename StrongIntName, typename ValueType> \
369 static inline constexpr bool operator op( \
370 StrongInt<StrongIntName, ValueType> id, \
371 typename StrongInt<StrongIntName, ValueType>::ValueType val) { \
372 return id.value() op val; \
373 } \
374 template <typename StrongIntName, typename ValueType> \
375 static inline constexpr bool operator op( \
376 typename StrongInt<StrongIntName, ValueType>::ValueType val, \
377 StrongInt<StrongIntName, ValueType> id) { \
378 return val op id.value(); \
379 }
386#undef STRONG_INT_TYPE_COMPARISON_OP
387
388// Support for-range loops. Enables easier looping over ranges of StrongInts,
389// especially looping over sub-ranges of StrongVectors.
390template <typename IntType>
392 public:
393 // Iterator over the indices.
395 public:
396 using value_type = IntType;
397 using difference_type = IntType;
398 using reference = const IntType&;
399 using pointer = const IntType*;
400 using iterator_category = std::input_iterator_tag;
401
402 explicit StrongIntRangeIterator(IntType initial) : current_(initial) {}
403 bool operator!=(const StrongIntRangeIterator& other) const {
404 return current_ != other.current_;
405 }
406 bool operator==(const StrongIntRangeIterator& other) const {
407 return current_ == other.current_;
408 }
409 value_type operator*() const { return current_; }
410 pointer operator->() const { return &current_; }
412 ++current_;
413 return *this;
414 }
416 StrongIntRangeIterator old_iter = *this;
417 ++current_;
418 return old_iter;
419 }
420
421 private:
422 IntType current_;
423 };
424
425 // Loops from IntType(0) up to (but not including) end.
426 explicit StrongIntRange(IntType end) : begin_(IntType(0)), end_(end) {}
427 // Loops from begin up to (but not including) end.
428 StrongIntRange(IntType begin, IntType end) : begin_(begin), end_(end) {}
429 StrongIntRangeIterator begin() const { return begin_; }
430 StrongIntRangeIterator end() const { return end_; }
431
432 private:
433 const StrongIntRangeIterator begin_;
434 const StrongIntRangeIterator end_;
435};
436
437template <typename IntType>
441
442template <typename IntType>
444 return StrongIntRange<IntType>(begin, end);
445}
446} // namespace util_intops
447
448// Allows it to be used as a key to hashable containers.
449namespace std {
450template <typename StrongIntName, typename ValueType>
451struct hash<util_intops::StrongInt<StrongIntName, ValueType>>
452 : util_intops::StrongInt<StrongIntName, ValueType>::Hasher {};
453} // namespace std
454
455#endif // OR_TOOLS_BASE_STRONG_INT_H_
bool operator!=(const StrongIntRangeIterator &other) const
Definition strong_int.h:403
bool operator==(const StrongIntRangeIterator &other) const
Definition strong_int.h:406
StrongIntRange(IntType end)
Loops from IntType(0) up to (but not including) end.
Definition strong_int.h:426
StrongIntRange(IntType begin, IntType end)
Loops from begin up to (but not including) end.
Definition strong_int.h:428
StrongIntRangeIterator end() const
Definition strong_int.h:430
StrongIntRangeIterator begin() const
Definition strong_int.h:429
constexpr ValueType value() const
Definition strong_int.h:217
constexpr ValType value() const
Definition strong_int.h:220
ThisType & operator=(ValueType arg_value)
Definition strong_int.h:280
constexpr StrongInt(ValueType value)
C'tor explicitly initializing from a ValueType.
Definition strong_int.h:207
constexpr bool operator!() const
Definition strong_int.h:254
ThisType & operator--()
Definition strong_int.h:244
StrongInt< StrongIntName, ValueType > ThisType
Definition strong_int.h:189
const ThisType operator++(int v)
Definition strong_int.h:239
constexpr const ThisType operator~() const
Definition strong_int.h:257
ThisType & operator++()
– UNARY OPERATORS -----------------------------------------------------—
Definition strong_int.h:235
constexpr const ThisType operator-() const
Definition strong_int.h:256
static constexpr absl::string_view TypeName()
Definition strong_int.h:191
const ThisType operator--(int v)
Definition strong_int.h:248
constexpr const ThisType operator+() const
Definition strong_int.h:255
STRONG_INT_TYPE_ASSIGNMENT_OP * STRONG_INT_TYPE_ASSIGNMENT_OP(/=);STRONG_INT_TYPE_ASSIGNMENT_OP(<<=
constexpr StrongInt()
Default c'tor initializing value_ to 0.
Definition strong_int.h:205
#define COMPILE_ASSERT(x, msg)
Definition macros.h:19
int64_t hash
STL namespace.
STRONG_INT_TYPE_COMPARISON_OP(==)
StrongIntRange< IntType > MakeStrongIntRange(IntType end)
Definition strong_int.h:438
void AbslStringify(Sink &sink, StrongInt< T... > arg)
Definition strong_int.h:306
std::ostream & operator<<(std::ostream &os, StrongInt< StrongIntName, ValueType > arg)
Definition strong_int.h:297
class util_intops::StrongInt ABSL_ATTRIBUTE_PACKED
std::optional< int64_t > end
#define STRONG_INT_TYPE_ASSIGNMENT_OP(op)
Definition strong_int.h:262
#define STRONG_INT_TYPE_ARITHMETIC_OP(op)
Definition strong_int.h:327
size_t operator()(const StrongInt &arg) const
Definition strong_int.h:198