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