OR-Tools  8.2
int_type.h
Go to the documentation of this file.
1// Copyright 2010-2018 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// IntType 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 IntType<IntTypeName, ValueType> (where ValueType assumes
19// valid scalar types such as int, uint, int32, etc) has the additional
20// property that it cannot be assigned to or constructed from other IntTypes
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_INT_TYPE(IntTypeName, ValueType);
34//
35// where:
36// IntTypeName: 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_INT_TYPE(GlobalDocID, int64);
44// DEFINE_INT_TYPE(LocalDocID, int64);
45// GlobalDocID global;
46// LocalDocID local;
47//
48// The class IntType prevents:
49//
50// 1) Assignments of other IntTypes with different IntTypeNames.
51//
52// global = local; <-- Fails to compile!
53// local = global; <-- Fails to compile!
54//
55// 2) Explicit/implicit conversion from an IntType to another IntType.
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 IntType to a native integer type.
65//
66// void GetGlobalDoc(int64 global) { ...
67// GetGlobalDoc(global); <-- Fails to compile!
68// GetGlobalDoc(local); <-- Fails to compile!
69//
70// void GetLocalDoc(int32 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 IntTypeName 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 IntType to be used
89// as key to hashable containers such as hash_map and hash_set.
90//
91// We suggest using the IntTypeIndexedContainer wrapper around google3's
92// FixedArray and STL vector (see int-type-indexed-container.h) if an IntType is
93// intended to be used as an index into these containers. These wrappers are
94// indexed in a type-safe manner using IntTypes 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_INT_TYPE(GlobalDocID, int64);
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_INT_TYPE(LocalDocID, int64);
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 IntType 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 IntType class. Keep in mind that the primary reason for the IntType
129// class is to prevent *accidental* mingling of similar logical integer types --
130// and not type casting from one type to another.
131//
132// DEFINE_INT_TYPE(GlobalDocID, int64);
133// DEFINE_INT_TYPE(LocalDocID, int64);
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 global) { ...
143// GetGlobalDoc(local.value()); <-- Compiles fine.
144
145#ifndef OR_TOOLS_BASE_INT_TYPE_H_
146#define OR_TOOLS_BASE_INT_TYPE_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/strings/string_view.h"
156#include "ortools/base/macros.h"
157
158namespace gtl {
159
160template <typename IntTypeName, typename _ValueType>
161class IntType;
162
163// Defines the IntType 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_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 ::gtl::IntType<int_type_name##_tag_, value_type> int_type_name;
171
172// Holds a integral value (of type ValueType) and behaves as a
173// ValueType by exposing assignment, unary, comparison, and arithmetic
174// operators.
175//
176// The template parameter IntTypeName defines the name for the int type and must
177// be unique within a binary (the convenient DEFINE_INT_TYPE macro at the end of
178// the file generates a unique IntTypeName). The parameter ValueType defines
179// the integer type value (see supported list above).
180//
181// This class is NOT thread-safe.
182template <typename IntTypeName, typename _ValueType>
183class IntType {
184 public:
185 typedef _ValueType ValueType; // for non-member operators
186 typedef IntType<IntTypeName, ValueType> ThisType; // Syntactic sugar.
187
188 static constexpr absl::string_view TypeName() {
189 return IntTypeName::TypeName();
190 }
191
192 // Note that this may change from time to time without notice.
193 struct Hasher {
194 size_t operator()(const IntType& arg) const {
195 return static_cast<size_t>(arg.value());
196 }
197 };
198
199 public:
200 // Default c'tor initializing value_ to 0.
201 constexpr IntType() : value_(0) {}
202 // C'tor explicitly initializing from a ValueType.
203 constexpr explicit IntType(ValueType value) : value_(value) {}
204
205 // IntType uses the default copy constructor, destructor and assign operator.
206 // The defaults are sufficient and omitting them allows the compiler to add
207 // the move constructor/assignment.
208
209 // -- ACCESSORS --------------------------------------------------------------
210 // The class provides a value() accessor returning the stored ValueType value_
211 // as well as a templatized accessor that is just a syntactic sugar for
212 // static_cast<T>(var.value());
213 constexpr ValueType value() const { return value_; }
214
215 template <typename ValType>
216 constexpr ValType value() const {
217 return static_cast<ValType>(value_);
218 }
219
220 // -- UNARY OPERATORS --------------------------------------------------------
221 ThisType& operator++() { // prefix ++
222 ++value_;
223 return *this;
224 }
225 const ThisType operator++(int v) { // postfix ++
226 ThisType temp(*this);
227 ++value_;
228 return temp;
229 }
230 ThisType& operator--() { // prefix --
231 --value_;
232 return *this;
233 }
234 const ThisType operator--(int v) { // postfix --
235 ThisType temp(*this);
236 --value_;
237 return temp;
238 }
239
240 constexpr bool operator!() const { return value_ == 0; }
241 constexpr const ThisType operator+() const { return ThisType(value_); }
242 constexpr const ThisType operator-() const { return ThisType(-value_); }
243 constexpr const ThisType operator~() const { return ThisType(~value_); }
244
245 // -- ASSIGNMENT OPERATORS ---------------------------------------------------
246 // We support the following assignment operators: =, +=, -=, *=, /=, <<=, >>=
247 // and %= for both ThisType and ValueType.
248#define INT_TYPE_ASSIGNMENT_OP(op) \
249 ThisType& operator op(const ThisType& arg_value) { \
250 value_ op arg_value.value(); \
251 return *this; \
252 } \
253 ThisType& operator op(ValueType arg_value) { \
254 value_ op arg_value; \
255 return *this; \
256 }
261 INT_TYPE_ASSIGNMENT_OP(<<=); // NOLINT
264#undef INT_TYPE_ASSIGNMENT_OP
265
267 value_ = arg_value;
268 return *this;
269 }
270
271 private:
272 // The integer value of type ValueType.
273 ValueType value_;
274
276 invalid_integer_type_for_id_type_);
278
279// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
280// We provide the << operator, primarily for logging purposes. Currently, there
281// seems to be no need for an >> operator.
282template <typename IntTypeName, typename ValueType>
283std::ostream& operator<<(std::ostream& os, // NOLINT
285 return os << arg.value();
286}
287
288// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
289// We support only the +, -, *, and / operators with the same IntType and
290// ValueType types. The reason is to allow simple manipulation on these IDs
291// when used as indices in vectors and arrays.
292//
293// NB: Although it is possible to do IntType * IntType and IntType / IntType,
294// it is probably non-sensical from a dimensionality analysis perspective.
295#define INT_TYPE_ARITHMETIC_OP(op) \
296 template <typename IntTypeName, typename ValueType> \
297 constexpr IntType<IntTypeName, ValueType> operator op( \
298 IntType<IntTypeName, ValueType> id_1, \
299 IntType<IntTypeName, ValueType> id_2) { \
300 return IntType<IntTypeName, ValueType>(id_1.value() op id_2.value()); \
301 } \
302 template <typename IntTypeName, typename ValueType> \
303 constexpr IntType<IntTypeName, ValueType> operator op( \
304 IntType<IntTypeName, ValueType> id, \
305 typename IntType<IntTypeName, ValueType>::ValueType arg_val) { \
306 return IntType<IntTypeName, ValueType>(id.value() op arg_val); \
307 } \
308 template <typename IntTypeName, typename ValueType> \
309 constexpr IntType<IntTypeName, ValueType> operator op( \
310 typename IntType<IntTypeName, ValueType>::ValueType arg_val, \
311 IntType<IntTypeName, ValueType> id) { \
312 return IntType<IntTypeName, ValueType>(arg_val op id.value()); \
313 }
318INT_TYPE_ARITHMETIC_OP(<<); // NOLINT
321#undef INT_TYPE_ARITHMETIC_OP
322
323// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
324// Static inline comparison operators. We allow all comparison operators among
325// the following types (OP \in [==, !=, <, <=, >, >=]:
326// IntType<IntTypeName, ValueType> OP IntType<IntTypeName, ValueType>
327// IntType<IntTypeName, ValueType> OP ValueType
328// ValueType OP IntType<IntTypeName, ValueType>
329#define INT_TYPE_COMPARISON_OP(op) \
330 template <typename IntTypeName, typename ValueType> \
331 static inline constexpr bool operator op( \
332 IntType<IntTypeName, ValueType> id_1, \
333 IntType<IntTypeName, ValueType> id_2) { \
334 return id_1.value() op id_2.value(); \
335 } \
336 template <typename IntTypeName, typename ValueType> \
337 static inline constexpr bool operator op( \
338 IntType<IntTypeName, ValueType> id, \
339 typename IntType<IntTypeName, ValueType>::ValueType val) { \
340 return id.value() op val; \
341 } \
342 template <typename IntTypeName, typename ValueType> \
343 static inline constexpr bool operator op( \
344 typename IntType<IntTypeName, ValueType>::ValueType val, \
345 IntType<IntTypeName, ValueType> id) { \
346 return val op id.value(); \
347 }
350INT_TYPE_COMPARISON_OP(<); // NOLINT
352INT_TYPE_COMPARISON_OP(>); // NOLINT
354#undef INT_TYPE_COMPARISON_OP
355
356} // namespace gtl
357
358// Allows it to be used as a key to hashable containers.
359namespace std {
360template <typename IntTypeName, typename ValueType>
361struct hash<gtl::IntType<IntTypeName, ValueType> >
362 : gtl::IntType<IntTypeName, ValueType>::Hasher {};
363} // namespace std
364
365#endif // OR_TOOLS_BASE_INT_TYPE_H_
INT_TYPE_ASSIGNMENT_OP(+=)
IntType< IntTypeName, ValueType > ThisType
Definition: int_type.h:186
INT_TYPE_ASSIGNMENT_OP(%=)
INT_TYPE_ASSIGNMENT_OP(-=)
constexpr IntType(ValueType value)
Definition: int_type.h:203
INT_TYPE_ASSIGNMENT_OP(> >=)
ThisType & operator=(ValueType arg_value)
Definition: int_type.h:266
INT_TYPE_ASSIGNMENT_OP * INT_TYPE_ASSIGNMENT_OP(/=);INT_TYPE_ASSIGNMENT_OP(<<=
const ThisType operator--(int v)
Definition: int_type.h:234
constexpr const ThisType operator+() const
Definition: int_type.h:241
_ValueType ValueType
Definition: int_type.h:185
ThisType & operator--()
Definition: int_type.h:230
constexpr const ThisType operator~() const
Definition: int_type.h:243
const ThisType operator++(int v)
Definition: int_type.h:225
constexpr const ThisType operator-() const
Definition: int_type.h:242
constexpr ValType value() const
Definition: int_type.h:216
constexpr bool operator!() const
Definition: int_type.h:240
ThisType & operator++()
Definition: int_type.h:221
constexpr IntType()
Definition: int_type.h:201
constexpr ValueType value() const
Definition: int_type.h:213
static constexpr absl::string_view TypeName()
Definition: int_type.h:188
int64 value
int64 hash
Definition: matrix_utils.cc:60
std::ostream & operator<<(std::ostream &os, IntType< IntTypeName, ValueType > arg)
Definition: int_type.h:283
INT_TYPE_ARITHMETIC_OP(+)
class gtl::IntType ABSL_ATTRIBUTE_PACKED
INT_TYPE_COMPARISON_OP(==)
STL namespace.
size_t operator()(const IntType &arg) const
Definition: int_type.h:194