OR-Tools  9.3
strong_int.h
Go to the documentation of this file.
1// Copyright 2010-2021 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// FixedArray and STL vector (see int-type-indexed-container.h) if an StrongInt
92// is intended to be used as an index into these containers. These wrappers are
93// indexed in a type-safe manner using StrongInts to ensure type-safety.
94//
95// NB: this implementation does not attempt to abide by or enforce dimensional
96// analysis on these scalar types.
97//
98// EXAMPLES --------------------------------------------------------------------
99//
100// DEFINE_STRONG_INT_TYPE(GlobalDocID, int64_t);
101// GlobalDocID global = 3;
102// std::cout << global; <-- Prints 3 to stdout.
103//
104// for (GlobalDocID i(0); i < global; ++i) {
105// std::cout << i;
106// } <-- Print(ln)s 0 1 2 to stdout
107//
108// DEFINE_STRONG_INT_TYPE(LocalDocID, int64_t);
109// LocalDocID local;
110// std::cout << local; <-- Prints 0 to stdout it
111// default
112// initializes the value to 0.
113//
114// local = 5;
115// local *= 2;
116// LocalDocID l(local);
117// std::cout << l + local; <-- Prints 20 to stdout.
118//
119// GenericSearchRequest request;
120// request.set_doc_id(global.value()); <-- Uses value() to extract the value
121// from the StrongInt class.
122//
123// REMARKS ---------------------------------------------------------------------
124//
125// The following bad usage is permissible although discouraged. Essentially, it
126// involves using the value*() accessors to extract the native integer type out
127// of the StrongInt class. Keep in mind that the primary reason for the
128// StrongInt class is to prevent *accidental* mingling of similar logical
129// integer types -- and not type casting from one type to another.
130//
131// DEFINE_STRONG_INT_TYPE(GlobalDocID, int64_t);
132// DEFINE_STRONG_INT_TYPE(LocalDocID, int64_t);
133// GlobalDocID global;
134// LocalDocID local;
135//
136// global = local.value(); <-- Compiles fine.
137//
138// void GetGlobalDoc(GlobalDocID global) { ...
139// GetGlobalDoc(local.value()); <-- Compiles fine.
140//
141// void GetGlobalDoc(int64_t global) { ...
142// GetGlobalDoc(local.value()); <-- Compiles fine.
143
144#ifndef OR_TOOLS_BASE_STRONG_INT_H_
145#define OR_TOOLS_BASE_STRONG_INT_H_
146
147#include <stddef.h>
148
149#include <functional>
150#include <iosfwd>
151#include <ostream> // NOLINT
152#include <type_traits>
153
154#include "absl/base/port.h"
155#include "absl/strings/string_view.h"
156#include "ortools/base/macros.h"
157
158namespace util_intops {
159
160template <typename StrongIntName, typename _ValueType>
161class StrongInt;
162
163// Defines the StrongInt using value_type and typedefs it to int_type_name.
164// The struct int_type_name ## _tag_ trickery is needed to ensure that a new
165// type is created per int_type_name.
166#define DEFINE_STRONG_INT_TYPE(int_type_name, value_type) \
167 struct int_type_name##_tag_ { \
168 static constexpr absl::string_view TypeName() { return #int_type_name; } \
169 }; \
170 typedef ::util_intops::StrongInt<int_type_name##_tag_, value_type> \
171 int_type_name;
172
173// Holds a integral value (of type ValueType) and behaves as a
174// ValueType by exposing assignment, unary, comparison, and arithmetic
175// operators.
176//
177// The template parameter StrongIntName defines the name for the int type and
178// must be unique within a binary (the convenient DEFINE_STRONG_INT macro at the
179// end of the file generates a unique StrongIntName). The parameter ValueType
180// defines the integer type value (see supported list above).
181//
182// This class is NOT thread-safe.
183template <typename StrongIntName, typename _ValueType>
185 public:
186 typedef _ValueType ValueType; // for non-member operators
188
189 static constexpr absl::string_view TypeName() {
190 return StrongIntName::TypeName();
191 }
192
193 // Note that this may change from time to time without notice.
194 struct Hasher {
195 size_t operator()(const StrongInt& arg) const {
196 return static_cast<size_t>(arg.value());
197 }
198 };
199
200 public:
201 // Default c'tor initializing value_ to 0.
202 constexpr StrongInt() : value_(0) {}
203 // C'tor explicitly initializing from a ValueType.
204 constexpr explicit StrongInt(ValueType value) : value_(value) {}
205
206 // StrongInt uses the default copy constructor, destructor and assign
207 // operator. The defaults are sufficient and omitting them allows the compiler
208 // to add the move constructor/assignment.
209
210 // -- ACCESSORS --------------------------------------------------------------
211 // The class provides a value() accessor returning the stored ValueType value_
212 // as well as a templatized accessor that is just a syntactic sugar for
213 // static_cast<T>(var.value());
214 constexpr ValueType value() const { return value_; }
215
216 template <typename ValType>
217 constexpr ValType value() const {
218 return static_cast<ValType>(value_);
219 }
220
221 // -- UNARY OPERATORS --------------------------------------------------------
222 ThisType& operator++() { // prefix ++
223 ++value_;
224 return *this;
225 }
226 const ThisType operator++(int v) { // postfix ++
227 ThisType temp(*this);
228 ++value_;
229 return temp;
230 }
231 ThisType& operator--() { // prefix --
232 --value_;
233 return *this;
234 }
235 const ThisType operator--(int v) { // postfix --
236 ThisType temp(*this);
237 --value_;
238 return temp;
239 }
240
241 constexpr bool operator!() const { return value_ == 0; }
242 constexpr const ThisType operator+() const { return ThisType(value_); }
243 constexpr const ThisType operator-() const { return ThisType(-value_); }
244 constexpr const ThisType operator~() const { return ThisType(~value_); }
245
246 // -- ASSIGNMENT OPERATORS ---------------------------------------------------
247 // We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
248 // and %= for both ThisType and ValueType.
249#define STRONG_INT_TYPE_ASSIGNMENT_OP(op) \
250 ThisType& operator op(const ThisType& arg_value) { \
251 value_ op arg_value.value(); \
252 return *this; \
253 } \
254 ThisType& operator op(ValueType arg_value) { \
255 value_ op arg_value; \
256 return *this; \
257 }
262 STRONG_INT_TYPE_ASSIGNMENT_OP(<<=); // NOLINT
265#undef STRONG_INT_TYPE_ASSIGNMENT_OP
266
268 value_ = arg_value;
269 return *this;
270 }
271
272 private:
273 // The integer value of type ValueType.
274 ValueType value_;
275
277 invalid_integer_type_for_id_type_);
279
280// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
281// We provide the << operator, primarily for logging purposes. Currently, there
282// seems to be no need for an >> operator.
283template <typename StrongIntName, typename ValueType>
284std::ostream& operator<<(std::ostream& os, // NOLINT
286 return os << arg.value();
287}
288
289// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
290// We support only the +, -, *, and / operators with the same StrongInt and
291// ValueType types. The reason is to allow simple manipulation on these IDs
292// when used as indices in vectors and arrays.
293//
294// NB: Although it is possible to do StrongInt * StrongInt and StrongInt /
295// StrongInt, it is probably non-sensical from a dimensionality analysis
296// perspective.
297#define STRONG_INT_TYPE_ARITHMETIC_OP(op) \
298 template <typename StrongIntName, typename ValueType> \
299 constexpr StrongInt<StrongIntName, ValueType> operator op( \
300 StrongInt<StrongIntName, ValueType> id_1, \
301 StrongInt<StrongIntName, ValueType> id_2) { \
302 return StrongInt<StrongIntName, ValueType>(id_1.value() op id_2.value()); \
303 } \
304 template <typename StrongIntName, typename ValueType> \
305 constexpr StrongInt<StrongIntName, ValueType> operator op( \
306 StrongInt<StrongIntName, ValueType> id, \
307 typename StrongInt<StrongIntName, ValueType>::ValueType arg_val) { \
308 return StrongInt<StrongIntName, ValueType>(id.value() op arg_val); \
309 } \
310 template <typename StrongIntName, typename ValueType> \
311 constexpr StrongInt<StrongIntName, ValueType> operator op( \
312 typename StrongInt<StrongIntName, ValueType>::ValueType arg_val, \
313 StrongInt<StrongIntName, ValueType> id) { \
314 return StrongInt<StrongIntName, ValueType>(arg_val op id.value()); \
315 }
323#undef STRONG_INT_TYPE_ARITHMETIC_OP
324
325// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
326// Static inline comparison operators. We allow all comparison operators among
327// the following types (OP \in [==, !=, <, <=, >, >=]:
328// StrongInt<StrongIntName, ValueType> OP StrongInt<StrongIntName, ValueType>
329// StrongInt<StrongIntName, ValueType> OP ValueType
330// ValueType OP StrongInt<StrongIntName, ValueType>
331#define STRONG_INT_TYPE_COMPARISON_OP(op) \
332 template <typename StrongIntName, typename ValueType> \
333 static inline constexpr bool operator op( \
334 StrongInt<StrongIntName, ValueType> id_1, \
335 StrongInt<StrongIntName, ValueType> id_2) { \
336 return id_1.value() op id_2.value(); \
337 } \
338 template <typename StrongIntName, typename ValueType> \
339 static inline constexpr bool operator op( \
340 StrongInt<StrongIntName, ValueType> id, \
341 typename StrongInt<StrongIntName, ValueType>::ValueType val) { \
342 return id.value() op val; \
343 } \
344 template <typename StrongIntName, typename ValueType> \
345 static inline constexpr bool operator op( \
346 typename StrongInt<StrongIntName, ValueType>::ValueType val, \
347 StrongInt<StrongIntName, ValueType> id) { \
348 return val op id.value(); \
349 }
356#undef STRONG_INT_TYPE_COMPARISON_OP
357
358} // namespace util_intops
359
360// Allows it to be used as a key to hashable containers.
361namespace std {
362template <typename StrongIntName, typename ValueType>
363struct hash<util_intops::StrongInt<StrongIntName, ValueType> >
364 : util_intops::StrongInt<StrongIntName, ValueType>::Hasher {};
365} // namespace std
366
367#endif // OR_TOOLS_BASE_STRONG_INT_H_
STRONG_INT_TYPE_ASSIGNMENT_OP * STRONG_INT_TYPE_ASSIGNMENT_OP(/=);STRONG_INT_TYPE_ASSIGNMENT_OP(<<=
ThisType & operator=(ValueType arg_value)
Definition: strong_int.h:267
const ThisType operator--(int v)
Definition: strong_int.h:235
constexpr const ThisType operator+() const
Definition: strong_int.h:242
ThisType & operator--()
Definition: strong_int.h:231
constexpr const ThisType operator~() const
Definition: strong_int.h:244
const ThisType operator++(int v)
Definition: strong_int.h:226
constexpr const ThisType operator-() const
Definition: strong_int.h:243
constexpr ValType value() const
Definition: strong_int.h:217
constexpr bool operator!() const
Definition: strong_int.h:241
constexpr StrongInt()
Definition: strong_int.h:202
StrongInt< StrongIntName, ValueType > ThisType
Definition: strong_int.h:187
ThisType & operator++()
Definition: strong_int.h:222
constexpr StrongInt(ValueType value)
Definition: strong_int.h:204
constexpr ValueType value() const
Definition: strong_int.h:214
static constexpr absl::string_view TypeName()
Definition: strong_int.h:189
int64_t value
int64_t hash
Definition: matrix_utils.cc:61
STL namespace.
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:284
size_t operator()(const StrongInt &arg) const
Definition: strong_int.h:195