Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
strong_integers.h
Go to the documentation of this file.
1// Copyright 2010-2025 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// Generates strongly typed integer types.
15//
16// StrongIndex is a simple template class mechanism for defining "logical"
17// index-like class types that support some of the same functionalities
18// as int, but which prevent assignment, construction, and
19// other operations from other similar integer-like types. Essentially, the
20// template class StrongIndex<StrongIndexName> has the additional
21// property that it cannot be assigned to or constructed from another
22// StrongIndex with a different StrongIndexName.
23//
24// Usage
25// DEFINE_STRONG_INDEX_TYPE(name);
26// where name is the desired (unique) name for the "logical" index type.
27//
28// StrongInt64 is a more general strong integer class based on int64_t.
29// It has the same general type safeness, and supports more integer operators.
30//
31// Usage
32// DEFINE_STRONG_INT64_TYPE(name);
33// where name is the desired (unique) name for the "logical" int64_t type.
34//
35// SUPPORTED OPERATIONS --------------------------------------------------------
36//
37// The StrongIndex type is limited and only supports following operators are
38// supported: unary: ++ (both prefix and postfix), comparison: ==, !=, <, <=, >,
39// >=; assignment: =, +=, -=,; stream: <<. Each operator allows the same
40// StrongIndexName and the int to be used on both left- and right-hand sides.
41//
42// The StrongInt64 type supports all integer operators across StrongInt64
43// with the same StrongIntegerName and int64_t.
44//
45// Both support an accessor value() returning the stored value.
46//
47// The classes also define hash functors that allows the strong types to be used
48// as key to hashable containers.
49
50#ifndef OR_TOOLS_UTIL_STRONG_INTEGERS_H_
51#define OR_TOOLS_UTIL_STRONG_INTEGERS_H_
52
53#include <stddef.h>
54
55#include <cstdint>
56#include <iosfwd>
57#include <limits>
58#include <ostream> // NOLINT
59
60#include "absl/base/attributes.h"
61#include "absl/base/port.h"
62#include "absl/strings/str_format.h"
63#include "absl/strings/string_view.h"
64
65namespace operations_research {
66
67// Defines the StrongIndex and typedefs it to index_type_name.
68//
69// Note: The struct index_type_name ## _index_tag_ trickery is needed to ensure
70// that a new type is created per index_type_name.
71#define DEFINE_STRONG_INDEX_TYPE(index_type_name) \
72 struct index_type_name##_index_tag_ { \
73 static constexpr absl::string_view TypeName() { return #index_type_name; } \
74 }; \
75 typedef ::operations_research::StrongIndex<index_type_name##_index_tag_> \
76 index_type_name;
77
78// Defines the StrongInt64 and typedefs it to integer_type_name.
79//
80// Note: The struct integer_type_name ## _integer_tag_ trickery is needed to
81// ensure that a new type is created per integer_type_name.
82#define DEFINE_STRONG_INT64_TYPE(integer_type_name) \
83 struct integer_type_name##_integer_tag_ { \
84 static constexpr absl::string_view TypeName() { \
85 return #integer_type_name; \
86 } \
87 }; \
88 typedef ::operations_research::StrongInt64<integer_type_name##_integer_tag_> \
89 integer_type_name;
90
91// ----------- Implementation ------------
92
93// Note: we need two classes as it is the only way to have different set of
94// operators on each class.
95// Note: We use to class to easily define a different set of operators for the
96// index and int64_t type.
97
98#define STRONG_ASSIGNMENT_OP(StrongClass, IntType, op) \
99 ThisType& operator op(const ThisType & arg_value) { \
100 value_ op arg_value.value(); \
101 return *this; \
102 } \
103 ThisType& operator op(IntType arg_value) { \
104 value_ op arg_value; \
105 return *this; \
106 }
107
108#define INCREMENT_AND_DECREMENT_OPERATORS \
109 ThisType& operator++() { \
110 ++value_; \
111 return *this; \
112 } \
113 const ThisType operator++(int) { \
114 ThisType temp(*this); \
115 ++value_; \
116 return temp; \
117 } \
118 ThisType& operator--() { \
119 --value_; \
120 return *this; \
121 } \
122 const ThisType operator--(int) { \
123 ThisType temp(*this); \
124 --value_; \
125 return temp; \
126 }
127
128// Holds an int value and behaves as an int by exposing assignment,
129// unary, comparison, and arithmetic operators.
130//
131// The template parameter StrongIndexName defines the name for the int type and
132// must be unique within a binary (the convenient DEFINE_STRONG_INDEX_TYPE macro
133// at the start of the file generates a unique StrongIndexName).
134//
135// This class is NOT thread-safe.
136template <typename StrongIndexName>
138 public:
139 typedef int ValueType; // Needed for StrongVector.
140 typedef StrongIndex<StrongIndexName> ThisType; // Syntactic sugar.
141
142 static constexpr absl::string_view TypeName() {
143 return StrongIndexName::TypeName();
144 }
145
146 struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher {
147 size_t operator()(const StrongIndex& x) const {
148 return static_cast<size_t>(x.value());
149 }
150 };
151
152 constexpr StrongIndex() : value_(0) {}
153
154 explicit constexpr StrongIndex(int value) : value_(value) {}
155
156 StrongIndex& operator=(int arg_value) {
157 value_ = arg_value;
158 return *this;
159 }
160
161 // The class provides a value() accessor returning the stored int value_
162 // as well as a templatized accessor that is just a syntactic sugar for
163 // static_cast<T>(var.value());
164 constexpr int value() const { return value_; }
165
166 template <typename ValType> // Needed for StrongVector.
167 constexpr ValType value() const {
168 return static_cast<ValType>(value_);
169 }
170
171 constexpr ThisType operator+() const { return ThisType(value_); }
172 constexpr ThisType operator-() const { return ThisType(-value_); }
173
175
178
179 private:
180 int value_;
181};
182
183// Holds an int64_t value and behaves as an int64_t by exposing assignment,
184// unary, comparison, and arithmetic operators.
185//
186// The template parameter StrongIntegerName defines the name for the int type
187// and must be unique within a binary (the convenient DEFINE_STRONG_INTEGER_TYPE
188// macro at the start of the file generates a unique StrongIntegerName).
189//
190// This class is NOT thread-safe.
191template <typename StrongIntegerName>
193 public:
194 typedef int64_t ValueType; // Needed for StrongVector.
195 typedef StrongInt64<StrongIntegerName> ThisType; // Syntactic sugar.
196
197 static constexpr absl::string_view TypeName() {
198 return StrongIntegerName::TypeName();
199 }
200
201 struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher {
202 size_t operator()(const StrongInt64& x) const {
203 return static_cast<size_t>(x.value());
204 }
205 };
206
207 constexpr StrongInt64() : value_(0) {}
208
209 // NOLINTBEGIN(google-explicit-constructor)
210 constexpr StrongInt64(int64_t value) : value_(value) {}
211 // NOLINTEND(google-explicit-constructor)
212
213 StrongInt64& operator=(int64_t arg_value) {
214 value_ = arg_value;
215 return *this;
216 }
217
218 constexpr int64_t value() const { return value_; }
219 int64_t* mutable_value() { return &value_; }
220
221 template <typename ValType> // Needed for StrongVector.
222 constexpr ValType value() const {
223 return static_cast<ValType>(value_);
224 }
225
227
228 constexpr ThisType operator+() const { return ThisType(value_); }
229 constexpr ThisType operator-() const { return ThisType(-value_); }
230 constexpr ThisType operator~() const { return ThisType(~value_); }
231
239
240 private:
241 int64_t value_;
242};
243
244#undef STRONG_ASSIGNMENT_OP
245#undef INCREMENT_AND_DECREMENT_OPERATORS
246
247// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
248// We provide the << operator, primarily for logging purposes. Currently, there
249// seems to be no need for an >> operator.
250template <typename StrongIndexName>
251std::ostream& operator<<(std::ostream& os, // NOLINT
253 return os << arg.value();
254}
255
256template <typename Sink, typename... T>
257void AbslStringify(Sink& sink, StrongIndex<T...> arg) {
258 absl::Format(&sink, "%v", arg.value());
259}
260
261template <typename StrongIntegerName>
262std::ostream& operator<<(std::ostream& os, // NOLINT
264 return os << arg.value();
265}
266
267template <typename Sink, typename... T>
268void AbslStringify(Sink& sink, StrongInt64<T...> arg) {
269 absl::Format(&sink, "%v", arg.value());
270}
271
272// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
273#define STRONG_TYPE_ARITHMETIC_OP(StrongType, IntType, op) \
274 template <typename StrongName> \
275 constexpr StrongType<StrongName> operator op(StrongType<StrongName> id_1, \
276 StrongType<StrongName> id_2) { \
277 return StrongType<StrongName>(id_1.value() op id_2.value()); \
278 } \
279 template <typename StrongName> \
280 constexpr StrongType<StrongName> operator op(StrongType<StrongName> id, \
281 IntType arg_val) { \
282 return StrongType<StrongName>(id.value() op arg_val); \
283 } \
284 template <typename StrongName> \
285 constexpr StrongType<StrongName> operator op(IntType arg_val, \
286 StrongType<StrongName> id) { \
287 return StrongType<StrongName>(arg_val op id.value()); \
288 }
289
294
302#undef STRONG_TYPE_ARITHMETIC_OP
303
304// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
305#define STRONG_TYPE_COMPARISON_OP(StrongType, IntType, op) \
306 template <typename StrongName> \
307 static inline constexpr bool operator op(StrongType<StrongName> id_1, \
308 StrongType<StrongName> id_2) { \
309 return id_1.value() op id_2.value(); \
310 } \
311 template <typename StrongName> \
312 static inline constexpr bool operator op(StrongType<StrongName> id, \
313 IntType val) { \
314 return id.value() op val; \
315 } \
316 template <typename StrongName> \
317 static inline constexpr bool operator op(IntType val, \
318 StrongType<StrongName> id) { \
319 return val op id.value(); \
320 }
321
324STRONG_TYPE_COMPARISON_OP(StrongIndex, int, <); // NOLINT
326STRONG_TYPE_COMPARISON_OP(StrongIndex, int, >); // NOLINT
328
331STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, <); // NOLINT
333STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, >); // NOLINT
335#undef STRONG_TYPE_COMPARISON_OP
336
337// -- ABSL HASHING SUPPORT -----------------------------------------------------
338template <typename StrongIndexName, typename H>
340 return H::combine(std::move(h), i.value());
341}
342
343template <typename StrongIntegerName, typename H>
345 return H::combine(std::move(h), i.value());
346}
347
348} // namespace operations_research
349
350// -- STD HASHING SUPPORT -----------------------------------------------------
351namespace std {
352template <typename Tag>
353struct hash<operations_research::StrongIndex<Tag>>
354 : ::operations_research::StrongIndex<Tag>::Hasher {};
355
356template <typename Tag>
357struct hash<operations_research::StrongInt64<Tag>>
358 : ::operations_research::StrongInt64<Tag>::Hasher {};
359} // namespace std
360
361// -- STD NUMERIC_LIMITS SUPPORT ----------------------------------------------
362namespace std {
363
364template <typename Tag>
365struct numeric_limits<operations_research::StrongIndex<Tag>> {
366 private:
367 using StrongIntT = operations_research::StrongIndex<Tag>;
368 using NativeTypeT = typename operations_research::StrongIndex<Tag>::ValueType;
369
370 public:
371 // NOLINTBEGIN(google3-readability-class-member-naming)
372 static constexpr bool is_specialized = true;
373 static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
374 static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
375 static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
376 static constexpr bool has_infinity =
377 numeric_limits<NativeTypeT>::has_infinity;
378 static constexpr bool has_quiet_NaN =
379 numeric_limits<NativeTypeT>::has_quiet_NaN;
380 static constexpr bool has_signaling_NaN =
381 numeric_limits<NativeTypeT>::has_signaling_NaN;
382 static constexpr float_denorm_style has_denorm =
383 numeric_limits<NativeTypeT>::has_denorm;
384 static constexpr bool has_denorm_loss =
385 numeric_limits<NativeTypeT>::has_denorm_loss;
386 static constexpr float_round_style round_style =
387 numeric_limits<NativeTypeT>::round_style;
388 static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
389 static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
390 static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
391 static constexpr int digits = numeric_limits<NativeTypeT>::digits;
392 static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
393 static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
394 static constexpr int radix = numeric_limits<NativeTypeT>::radix;
395 static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
396 static constexpr int min_exponent10 =
397 numeric_limits<NativeTypeT>::min_exponent10;
398 static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
399 static constexpr int max_exponent10 =
400 numeric_limits<NativeTypeT>::max_exponent10;
401 static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
402 static constexpr bool tinyness_before =
403 numeric_limits<NativeTypeT>::tinyness_before;
404 // NOLINTEND(google3-readability-class-member-naming)
405
406 static constexpr StrongIntT(min)() {
407 return StrongIntT(numeric_limits<NativeTypeT>::min());
408 }
409 static constexpr StrongIntT lowest() {
410 return StrongIntT(numeric_limits<NativeTypeT>::lowest());
411 }
412 static constexpr StrongIntT(max)() {
413 return StrongIntT(numeric_limits<NativeTypeT>::max());
414 }
415 static constexpr StrongIntT epsilon() { return StrongIntT(); }
416 static constexpr StrongIntT round_error() { return StrongIntT(); }
417 static constexpr StrongIntT infinity() { return StrongIntT(); }
418 static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
419 static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
420 static constexpr StrongIntT denorm_min() { return StrongIntT(); }
421};
422
423template <typename Tag>
424struct numeric_limits<operations_research::StrongInt64<Tag>> {
425 private:
426 using StrongIntT = operations_research::StrongInt64<Tag>;
427 using NativeTypeT = typename operations_research::StrongInt64<Tag>::ValueType;
428
429 public:
430 // NOLINTBEGIN(google3-readability-class-member-naming)
431 static constexpr bool is_specialized = true;
432 static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
433 static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
434 static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
435 static constexpr bool has_infinity =
436 numeric_limits<NativeTypeT>::has_infinity;
437 static constexpr bool has_quiet_NaN =
438 numeric_limits<NativeTypeT>::has_quiet_NaN;
439 static constexpr bool has_signaling_NaN =
440 numeric_limits<NativeTypeT>::has_signaling_NaN;
441 static constexpr float_denorm_style has_denorm =
442 numeric_limits<NativeTypeT>::has_denorm;
443 static constexpr bool has_denorm_loss =
444 numeric_limits<NativeTypeT>::has_denorm_loss;
445 static constexpr float_round_style round_style =
446 numeric_limits<NativeTypeT>::round_style;
447 static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
448 static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
449 static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
450 static constexpr int digits = numeric_limits<NativeTypeT>::digits;
451 static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
452 static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
453 static constexpr int radix = numeric_limits<NativeTypeT>::radix;
454 static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
455 static constexpr int min_exponent10 =
456 numeric_limits<NativeTypeT>::min_exponent10;
457 static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
458 static constexpr int max_exponent10 =
459 numeric_limits<NativeTypeT>::max_exponent10;
460 static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
461 static constexpr bool tinyness_before =
462 numeric_limits<NativeTypeT>::tinyness_before;
463 // NOLINTEND(google3-readability-class-member-naming)
464
465 static constexpr StrongIntT(min)() {
466 return StrongIntT(numeric_limits<NativeTypeT>::min());
467 }
468 static constexpr StrongIntT lowest() {
469 return StrongIntT(numeric_limits<NativeTypeT>::lowest());
470 }
471 static constexpr StrongIntT(max)() {
472 return StrongIntT(numeric_limits<NativeTypeT>::max());
473 }
474 static constexpr StrongIntT epsilon() { return StrongIntT(); }
475 static constexpr StrongIntT round_error() { return StrongIntT(); }
476 static constexpr StrongIntT infinity() { return StrongIntT(); }
477 static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
478 static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
479 static constexpr StrongIntT denorm_min() { return StrongIntT(); }
480};
481
482} // namespace std
483
484#endif // OR_TOOLS_UTIL_STRONG_INTEGERS_H_
StrongIndex & operator=(int arg_value)
STRONG_ASSIGNMENT_OP(StrongIndex, int,+=)
struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher
constexpr ThisType operator-() const
StrongIndex< StrongIndexName > ThisType
constexpr ThisType operator+() const
constexpr ValType value() const
STRONG_ASSIGNMENT_OP(StrongIndex, int, -=)
static constexpr absl::string_view TypeName()
StrongInt64< StrongIntegerName > ThisType
constexpr ValType value() const
constexpr StrongInt64(int64_t value)
NOLINTBEGIN(google-explicit-constructor)
constexpr ThisType operator-() const
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, *=)
static constexpr absl::string_view TypeName()
constexpr ThisType operator~() const
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t,+=)
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t,<<=)
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, -=)
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, %=)
struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher
constexpr ThisType operator+() const
StrongInt64 & operator=(int64_t arg_value)
NOLINTEND(google-explicit-constructor)
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, > >=)
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t,/=)
In SWIG mode, we don't want anything besides these top-level includes.
H AbslHashValue(H h, const StrongIndex< StrongIndexName > &i)
– ABSL HASHING SUPPORT --------------------------------------------------—
std::ostream & operator<<(std::ostream &out, const Assignment &assignment)
void AbslStringify(Sink &sink, StrongIndex< T... > arg)
STL namespace.
#define STRONG_TYPE_COMPARISON_OP(StrongType, IntType, op)
– NON-MEMBER COMPARISON OPERATORS ---------------------------------------—
#define STRONG_TYPE_ARITHMETIC_OP(StrongType, IntType, op)
– NON-MEMBER ARITHMETIC OPERATORS ---------------------------------------—
static constexpr StrongIntT min()
NOLINTEND(google3-readability-class-member-naming)
static constexpr bool is_specialized
NOLINTBEGIN(google3-readability-class-member-naming)
static constexpr bool is_specialized
NOLINTBEGIN(google3-readability-class-member-naming)
static constexpr StrongIntT min()
NOLINTEND(google3-readability-class-member-naming)