Google OR-Tools v9.11
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-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// 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
220 template <typename ValType> // Needed for StrongVector.
221 constexpr ValType value() const {
222 return static_cast<ValType>(value_);
223 }
224
226
227 constexpr ThisType operator+() const { return ThisType(value_); }
228 constexpr ThisType operator-() const { return ThisType(-value_); }
229 constexpr ThisType operator~() const { return ThisType(~value_); }
230
238
239 private:
240 int64_t value_;
241};
242
243#undef STRONG_ASSIGNMENT_OP
244#undef INCREMENT_AND_DECREMENT_OPERATORS
245
246// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
247// We provide the << operator, primarily for logging purposes. Currently, there
248// seems to be no need for an >> operator.
249template <typename StrongIndexName>
250std::ostream& operator<<(std::ostream& os, // NOLINT
252 return os << arg.value();
253}
254
255template <typename Sink, typename... T>
256void AbslStringify(Sink& sink, StrongIndex<T...> arg) {
257 absl::Format(&sink, "%v", arg.value());
258}
259
260template <typename StrongIntegerName>
261std::ostream& operator<<(std::ostream& os, // NOLINT
263 return os << arg.value();
264}
265
266template <typename Sink, typename... T>
267void AbslStringify(Sink& sink, StrongInt64<T...> arg) {
268 absl::Format(&sink, "%v", arg.value());
269}
270
271// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
272#define STRONG_TYPE_ARITHMETIC_OP(StrongType, IntType, op) \
273 template <typename StrongName> \
274 constexpr StrongType<StrongName> operator op(StrongType<StrongName> id_1, \
275 StrongType<StrongName> id_2) { \
276 return StrongType<StrongName>(id_1.value() op id_2.value()); \
277 } \
278 template <typename StrongName> \
279 constexpr StrongType<StrongName> operator op(StrongType<StrongName> id, \
280 IntType arg_val) { \
281 return StrongType<StrongName>(id.value() op arg_val); \
282 } \
283 template <typename StrongName> \
284 constexpr StrongType<StrongName> operator op(IntType arg_val, \
285 StrongType<StrongName> id) { \
286 return StrongType<StrongName>(arg_val op id.value()); \
287 }
288
293
301#undef STRONG_TYPE_ARITHMETIC_OP
302
303// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
304#define STRONG_TYPE_COMPARISON_OP(StrongType, IntType, op) \
305 template <typename StrongName> \
306 static inline constexpr bool operator op(StrongType<StrongName> id_1, \
307 StrongType<StrongName> id_2) { \
308 return id_1.value() op id_2.value(); \
309 } \
310 template <typename StrongName> \
311 static inline constexpr bool operator op(StrongType<StrongName> id, \
312 IntType val) { \
313 return id.value() op val; \
314 } \
315 template <typename StrongName> \
316 static inline constexpr bool operator op(IntType val, \
317 StrongType<StrongName> id) { \
318 return val op id.value(); \
319 }
320
323STRONG_TYPE_COMPARISON_OP(StrongIndex, int, <); // NOLINT
325STRONG_TYPE_COMPARISON_OP(StrongIndex, int, >); // NOLINT
327
330STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, <); // NOLINT
332STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, >); // NOLINT
334#undef STRONG_TYPE_COMPARISON_OP
335
336// -- ABSL HASHING SUPPORT -----------------------------------------------------
337template <typename StrongIndexName, typename H>
339 return H::combine(std::move(h), i.value());
340}
341
342template <typename StrongIntegerName, typename H>
344 return H::combine(std::move(h), i.value());
345}
346
347} // namespace operations_research
348
349// -- STD HASHING SUPPORT -----------------------------------------------------
350namespace std {
351template <typename Tag>
352struct hash<operations_research::StrongIndex<Tag>>
353 : ::operations_research::StrongIndex<Tag>::Hasher {};
354
355template <typename Tag>
356struct hash<operations_research::StrongInt64<Tag>>
357 : ::operations_research::StrongInt64<Tag>::Hasher {};
358} // namespace std
359
360// -- STD NUMERIC_LIMITS SUPPORT ----------------------------------------------
361namespace std {
362
363template <typename Tag>
364struct numeric_limits<operations_research::StrongIndex<Tag>> {
365 private:
367 using NativeTypeT = typename operations_research::StrongIndex<Tag>::ValueType;
368
369 public:
370 // NOLINTBEGIN(google3-readability-class-member-naming)
371 static constexpr bool is_specialized = true;
372 static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
373 static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
374 static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
375 static constexpr bool has_infinity =
376 numeric_limits<NativeTypeT>::has_infinity;
377 static constexpr bool has_quiet_NaN =
378 numeric_limits<NativeTypeT>::has_quiet_NaN;
379 static constexpr bool has_signaling_NaN =
380 numeric_limits<NativeTypeT>::has_signaling_NaN;
381 static constexpr float_denorm_style has_denorm =
382 numeric_limits<NativeTypeT>::has_denorm;
383 static constexpr bool has_denorm_loss =
384 numeric_limits<NativeTypeT>::has_denorm_loss;
385 static constexpr float_round_style round_style =
386 numeric_limits<NativeTypeT>::round_style;
387 static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
388 static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
389 static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
390 static constexpr int digits = numeric_limits<NativeTypeT>::digits;
391 static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
392 static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
393 static constexpr int radix = numeric_limits<NativeTypeT>::radix;
394 static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
395 static constexpr int min_exponent10 =
396 numeric_limits<NativeTypeT>::min_exponent10;
397 static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
398 static constexpr int max_exponent10 =
399 numeric_limits<NativeTypeT>::max_exponent10;
400 static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
401 static constexpr bool tinyness_before =
402 numeric_limits<NativeTypeT>::tinyness_before;
403 // NOLINTEND(google3-readability-class-member-naming)
404
405 static constexpr StrongIntT(min)() {
406 return StrongIntT(numeric_limits<NativeTypeT>::min());
407 }
408 static constexpr StrongIntT lowest() {
409 return StrongIntT(numeric_limits<NativeTypeT>::lowest());
410 }
411 static constexpr StrongIntT(max)() {
412 return StrongIntT(numeric_limits<NativeTypeT>::max());
413 }
414 static constexpr StrongIntT epsilon() { return StrongIntT(); }
415 static constexpr StrongIntT round_error() { return StrongIntT(); }
416 static constexpr StrongIntT infinity() { return StrongIntT(); }
417 static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
418 static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
419 static constexpr StrongIntT denorm_min() { return StrongIntT(); }
420};
421
422template <typename Tag>
423struct numeric_limits<operations_research::StrongInt64<Tag>> {
424 private:
426 using NativeTypeT = typename operations_research::StrongInt64<Tag>::ValueType;
427
428 public:
429 // NOLINTBEGIN(google3-readability-class-member-naming)
430 static constexpr bool is_specialized = true;
431 static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
432 static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
433 static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
434 static constexpr bool has_infinity =
435 numeric_limits<NativeTypeT>::has_infinity;
436 static constexpr bool has_quiet_NaN =
437 numeric_limits<NativeTypeT>::has_quiet_NaN;
438 static constexpr bool has_signaling_NaN =
439 numeric_limits<NativeTypeT>::has_signaling_NaN;
440 static constexpr float_denorm_style has_denorm =
441 numeric_limits<NativeTypeT>::has_denorm;
442 static constexpr bool has_denorm_loss =
443 numeric_limits<NativeTypeT>::has_denorm_loss;
444 static constexpr float_round_style round_style =
445 numeric_limits<NativeTypeT>::round_style;
446 static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
447 static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
448 static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
449 static constexpr int digits = numeric_limits<NativeTypeT>::digits;
450 static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
451 static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
452 static constexpr int radix = numeric_limits<NativeTypeT>::radix;
453 static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
454 static constexpr int min_exponent10 =
455 numeric_limits<NativeTypeT>::min_exponent10;
456 static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
457 static constexpr int max_exponent10 =
458 numeric_limits<NativeTypeT>::max_exponent10;
459 static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
460 static constexpr bool tinyness_before =
461 numeric_limits<NativeTypeT>::tinyness_before;
462 // NOLINTEND(google3-readability-class-member-naming)
463
464 static constexpr StrongIntT(min)() {
465 return StrongIntT(numeric_limits<NativeTypeT>::min());
466 }
467 static constexpr StrongIntT lowest() {
468 return StrongIntT(numeric_limits<NativeTypeT>::lowest());
469 }
470 static constexpr StrongIntT(max)() {
471 return StrongIntT(numeric_limits<NativeTypeT>::max());
472 }
473 static constexpr StrongIntT epsilon() { return StrongIntT(); }
474 static constexpr StrongIntT round_error() { return StrongIntT(); }
475 static constexpr StrongIntT infinity() { return StrongIntT(); }
476 static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
477 static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
478 static constexpr StrongIntT denorm_min() { return StrongIntT(); }
479};
480
481} // namespace std
482
483#endif // OR_TOOLS_UTIL_STRONG_INTEGERS_H_
int64_t max
int64_t min
StrongIndex & operator=(int arg_value)
STRONG_ASSIGNMENT_OP(StrongIndex, int,+=)
struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher
constexpr ThisType operator-() const
constexpr ThisType operator+() const
constexpr ValType value() const
StrongIndex< StrongIndexName > ThisType
STRONG_ASSIGNMENT_OP(StrongIndex, int, -=)
static constexpr absl::string_view TypeName()
constexpr int64_t value() const
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,/=)
StrongInt64< StrongIntegerName > ThisType
int64_t hash
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.
const Variable x
Definition qp_tests.cc:127
#define STRONG_TYPE_COMPARISON_OP(StrongType, IntType, op)
– NON-MEMBER COMPARISON OPERATORS ---------------------------------------—
#define STRONG_TYPE_ARITHMETIC_OP(StrongType, IntType, op)
– NON-MEMBER ARITHMETIC OPERATORS ---------------------------------------—