OR-Tools  9.6
strong_int.h
Go to the documentation of this file.
1 // Copyright 2010-2022 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/string_view.h"
157 #include "ortools/base/macros.h"
158 
159 namespace util_intops {
160 
161 template <typename StrongIntName, typename _ValueType>
162 class StrongInt;
163 
164 // Defines the StrongInt using value_type and typedefs it to int_type_name.
165 // The struct int_type_name ## _tag_ trickery is needed to ensure that a new
166 // type is created per int_type_name.
167 #define DEFINE_STRONG_INT_TYPE(int_type_name, value_type) \
168  struct int_type_name##_tag_ { \
169  static constexpr absl::string_view TypeName() { return #int_type_name; } \
170  }; \
171  typedef ::util_intops::StrongInt<int_type_name##_tag_, value_type> \
172  int_type_name;
173 
174 // Holds a integral value (of type ValueType) and behaves as a
175 // ValueType by exposing assignment, unary, comparison, and arithmetic
176 // operators.
177 //
178 // The template parameter StrongIntName defines the name for the int type and
179 // must be unique within a binary (the convenient DEFINE_STRONG_INT macro at the
180 // end of the file generates a unique StrongIntName). The parameter ValueType
181 // defines the integer type value (see supported list above).
182 //
183 // This class is NOT thread-safe.
184 template <typename StrongIntName, typename _ValueType>
185 class StrongInt {
186  public:
187  typedef _ValueType ValueType; // for non-member operators
188  typedef StrongInt<StrongIntName, ValueType> ThisType; // Syntactic sugar.
189 
190  static constexpr absl::string_view TypeName() {
191  return StrongIntName::TypeName();
192  }
193 
194  // Note that this may change from time to time without notice.
195  // See .
196  struct Hasher {
197  size_t operator()(const StrongInt& arg) const {
198  return static_cast<size_t>(arg.value());
199  }
200  };
201 
202  public:
203  // Default c'tor initializing value_ to 0.
204  constexpr StrongInt() : value_(0) {}
205  // C'tor explicitly initializing from a ValueType.
206  constexpr explicit StrongInt(ValueType value) : value_(value) {}
207 
208  // StrongInt uses the default copy constructor, destructor and assign
209  // operator. The defaults are sufficient and omitting them allows the compiler
210  // to add the move constructor/assignment.
211 
212  // -- ACCESSORS --------------------------------------------------------------
213  // The class provides a value() accessor returning the stored ValueType value_
214  // as well as a templatized accessor that is just a syntactic sugar for
215  // static_cast<T>(var.value());
216  constexpr ValueType value() const { return value_; }
217 
218  template <typename ValType>
219  constexpr ValType value() const {
220  return static_cast<ValType>(value_);
221  }
222 
223  // -- UNARY OPERATORS --------------------------------------------------------
224  ThisType& operator++() { // prefix ++
225  ++value_;
226  return *this;
227  }
228  const ThisType operator++(int v) { // postfix ++
229  ThisType temp(*this);
230  ++value_;
231  return temp;
232  }
233  ThisType& operator--() { // prefix --
234  --value_;
235  return *this;
236  }
237  const ThisType operator--(int v) { // postfix --
238  ThisType temp(*this);
239  --value_;
240  return temp;
241  }
242 
243  constexpr bool operator!() const { return value_ == 0; }
244  constexpr const ThisType operator+() const { return ThisType(value_); }
245  constexpr const ThisType operator-() const { return ThisType(-value_); }
246  constexpr const ThisType operator~() const { return ThisType(~value_); }
247 
248  // -- ASSIGNMENT OPERATORS ---------------------------------------------------
249  // We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
250  // and %= for both ThisType and ValueType.
251 #define STRONG_INT_TYPE_ASSIGNMENT_OP(op) \
252  ThisType& operator op(const ThisType& arg_value) { \
253  value_ op arg_value.value(); \
254  return *this; \
255  } \
256  ThisType& operator op(ValueType arg_value) { \
257  value_ op arg_value; \
258  return *this; \
259  }
264  STRONG_INT_TYPE_ASSIGNMENT_OP(<<=); // NOLINT
267 #undef STRONG_INT_TYPE_ASSIGNMENT_OP
268 
270  value_ = arg_value;
271  return *this;
272  }
273 
274  private:
275  // The integer value of type ValueType.
276  ValueType value_;
277 
278  COMPILE_ASSERT(std::is_integral<ValueType>::value,
279  invalid_integer_type_for_id_type_);
281 
282 // -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
283 // We provide the << operator, primarily for logging purposes. Currently, there
284 // seems to be no need for an >> operator.
285 template <typename StrongIntName, typename ValueType>
286 std::ostream& operator<<(std::ostream& os, // NOLINT
288  return os << arg.value();
289 }
290 
291 // -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
292 // We support only the +, -, *, and / operators with the same StrongInt and
293 // ValueType types. The reason is to allow simple manipulation on these IDs
294 // when used as indices in vectors and arrays.
295 //
296 // NB: Although it is possible to do StrongInt * StrongInt and StrongInt /
297 // StrongInt, it is probably non-sensical from a dimensionality analysis
298 // perspective.
299 #define STRONG_INT_TYPE_ARITHMETIC_OP(op) \
300  template <typename StrongIntName, typename ValueType> \
301  constexpr StrongInt<StrongIntName, ValueType> operator op( \
302  StrongInt<StrongIntName, ValueType> id_1, \
303  StrongInt<StrongIntName, ValueType> id_2) { \
304  return StrongInt<StrongIntName, ValueType>(id_1.value() op id_2.value()); \
305  } \
306  template <typename StrongIntName, typename ValueType> \
307  constexpr StrongInt<StrongIntName, ValueType> operator op( \
308  StrongInt<StrongIntName, ValueType> id, \
309  typename StrongInt<StrongIntName, ValueType>::ValueType arg_val) { \
310  return StrongInt<StrongIntName, ValueType>(id.value() op arg_val); \
311  } \
312  template <typename StrongIntName, typename ValueType> \
313  constexpr StrongInt<StrongIntName, ValueType> operator op( \
314  typename StrongInt<StrongIntName, ValueType>::ValueType arg_val, \
315  StrongInt<StrongIntName, ValueType> id) { \
316  return StrongInt<StrongIntName, ValueType>(arg_val op id.value()); \
317  }
322 STRONG_INT_TYPE_ARITHMETIC_OP(<<); // NOLINT
325 #undef STRONG_INT_TYPE_ARITHMETIC_OP
326 
327 // -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
328 // Static inline comparison operators. We allow all comparison operators among
329 // the following types (OP \in [==, !=, <, <=, >, >=]:
330 // StrongInt<StrongIntName, ValueType> OP StrongInt<StrongIntName, ValueType>
331 // StrongInt<StrongIntName, ValueType> OP ValueType
332 // ValueType OP StrongInt<StrongIntName, ValueType>
333 #define STRONG_INT_TYPE_COMPARISON_OP(op) \
334  template <typename StrongIntName, typename ValueType> \
335  static inline constexpr bool operator op( \
336  StrongInt<StrongIntName, ValueType> id_1, \
337  StrongInt<StrongIntName, ValueType> id_2) { \
338  return id_1.value() op id_2.value(); \
339  } \
340  template <typename StrongIntName, typename ValueType> \
341  static inline constexpr bool operator op( \
342  StrongInt<StrongIntName, ValueType> id, \
343  typename StrongInt<StrongIntName, ValueType>::ValueType val) { \
344  return id.value() op val; \
345  } \
346  template <typename StrongIntName, typename ValueType> \
347  static inline constexpr bool operator op( \
348  typename StrongInt<StrongIntName, ValueType>::ValueType val, \
349  StrongInt<StrongIntName, ValueType> id) { \
350  return val op id.value(); \
351  }
354 STRONG_INT_TYPE_COMPARISON_OP(<); // NOLINT
356 STRONG_INT_TYPE_COMPARISON_OP(>); // NOLINT
358 #undef STRONG_INT_TYPE_COMPARISON_OP
359 
360 // Support for-range loops. Enables easier looping over ranges of StrongInts,
361 // especially looping over sub-ranges of StrongVectors.
362 template <typename IntType>
364  public:
365  // Iterator over the indices.
367  public:
368  using value_type = IntType;
369  using difference_type = IntType;
370  using reference = const IntType&;
371  using pointer = const IntType*;
372  using iterator_category = std::input_iterator_tag;
373 
374  explicit StrongIntRangeIterator(IntType initial) : current_(initial) {}
375  bool operator!=(const StrongIntRangeIterator& other) const {
376  return current_ != other.current_;
377  }
378  bool operator==(const StrongIntRangeIterator& other) const {
379  return current_ == other.current_;
380  }
381  value_type operator*() const { return current_; }
382  pointer operator->() const { return &current_; }
384  ++current_;
385  return *this;
386  }
388  StrongIntRangeIterator old_iter = *this;
389  ++current_;
390  return old_iter;
391  }
392 
393  private:
394  IntType current_;
395  };
396 
397  // Loops from IntType(0) up to (but not including) end.
398  explicit StrongIntRange(IntType end) : begin_(IntType(0)), end_(end) {}
399  // Loops from begin up to (but not including) end.
400  StrongIntRange(IntType begin, IntType end) : begin_(begin), end_(end) {}
401  StrongIntRangeIterator begin() const { return begin_; }
402  StrongIntRangeIterator end() const { return end_; }
403 
404  private:
405  const StrongIntRangeIterator begin_;
406  const StrongIntRangeIterator end_;
407 };
408 
409 template <typename IntType>
412 }
413 
414 template <typename IntType>
416  return StrongIntRange<IntType>(begin, end);
417 }
418 } // namespace util_intops
419 
420 // Allows it to be used as a key to hashable containers.
421 namespace std {
422 template <typename StrongIntName, typename ValueType>
423 struct hash<util_intops::StrongInt<StrongIntName, ValueType> >
424  : util_intops::StrongInt<StrongIntName, ValueType>::Hasher {};
425 } // namespace std
426 
427 #endif // OR_TOOLS_BASE_STRONG_INT_H_
const ThisType operator--(int v)
Definition: strong_int.h:237
constexpr const ThisType operator+() const
Definition: strong_int.h:244
constexpr const ThisType operator~() const
Definition: strong_int.h:246
ThisType & operator--()
Definition: strong_int.h:233
ThisType & operator++()
Definition: strong_int.h:224
const ThisType operator++(int v)
Definition: strong_int.h:228
constexpr const ThisType operator-() const
Definition: strong_int.h:245
constexpr ValType value() const
Definition: strong_int.h:219
constexpr bool operator!() const
Definition: strong_int.h:243
constexpr StrongInt()
Definition: strong_int.h:204
STRONG_INT_TYPE_ASSIGNMENT_OP * STRONG_INT_TYPE_ASSIGNMENT_OP(/=);STRONG_INT_TYPE_ASSIGNMENT_OP(<<=
StrongInt< StrongIntName, ValueType > ThisType
Definition: strong_int.h:188
ThisType & operator=(ValueType arg_value)
Definition: strong_int.h:269
constexpr StrongInt(ValueType value)
Definition: strong_int.h:206
constexpr ValueType value() const
Definition: strong_int.h:216
static constexpr absl::string_view TypeName()
Definition: strong_int.h:190
bool operator!=(const StrongIntRangeIterator &other) const
Definition: strong_int.h:375
bool operator==(const StrongIntRangeIterator &other) const
Definition: strong_int.h:378
StrongIntRangeIterator begin() const
Definition: strong_int.h:401
StrongIntRangeIterator end() const
Definition: strong_int.h:402
StrongIntRange(IntType begin, IntType end)
Definition: strong_int.h:400
int64_t value
int64_t hash
Definition: matrix_utils.cc:63
STRONG_INT_TYPE_ARITHMETIC_OP(+)
class util_intops::StrongInt ABSL_ATTRIBUTE_PACKED
STRONG_INT_TYPE_COMPARISON_OP(==)
std::ostream & operator<<(std::ostream &os, StrongInt< StrongIntName, ValueType > arg)
Definition: strong_int.h:286
StrongIntRange< IntType > MakeStrongIntRange(IntType end)
Definition: strong_int.h:410
std::optional< int64_t > end
size_t operator()(const StrongInt &arg) const
Definition: strong_int.h:197