aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/core/lib/gtl/int_type.h
blob: d3fcb08d384308482b70b188ec0e2cb06c61562b (plain)
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
// #status: LEGACY
// #category: Miscellaneous
// #summary: Integral types; prefer util/intops/strong_int.h
// #bugs: Infrastructure > C++ Library Team > util
//
// IntType 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 IntType<IntTypeName, ValueType> (where ValueType assumes
// valid scalar types such as int, uint, int32, etc) has the additional
// property that it cannot be assigned to or constructed from other IntTypes
// 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_INT_TYPE(IntTypeName, ValueType);
//
//  where:
//    IntTypeName: is the desired (unique) name for the "logical" integer type.
//    ValueType: is one of the integral types as defined by base::is_integral
//               (see base/type_traits.h).
//
// DISALLOWED OPERATIONS / TYPE-SAFETY ENFORCEMENT -----------------------------
//
//  Consider these definitions and variable declarations:
//    DEFINE_INT_TYPE(GlobalDocID, int64);
//    DEFINE_INT_TYPE(LocalDocID, int64);
//    GlobalDocID global;
//    LocalDocID local;
//
//  The class IntType prevents:
//
//  1) Assignments of other IntTypes with different IntTypeNames.
//
//    global = local;                  <-- Fails to compile!
//    local = global;                  <-- Fails to compile!
//
//  2) Explicit/implicit conversion from an IntType to another IntType.
//
//    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 IntType to a native integer type.
//
//    void GetGlobalDoc(int64 global) { ...
//    GetGlobalDoc(global);            <-- Fails to compile!
//    GetGlobalDoc(local);             <-- Fails to compile!
//
//    void GetLocalDoc(int32 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 IntTypeName 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 IntType to be used
// as key to hashable containers such as std::unordered_map and
// std::unordered_set.
//
// We suggest using the IntTypeIndexedContainer wrapper around FixedArray and
// STL vector (see int-type-indexed-container.h) if an IntType is intended to
// be used as an index into these containers.  These wrappers are indexed in a
// type-safe manner using IntTypes to ensure type-safety.
//
// NB: this implementation does not attempt to abide by or enforce dimensional
// analysis on these scalar types.
//
// EXAMPLES --------------------------------------------------------------------
//
//    DEFINE_INT_TYPE(GlobalDocID, int64);
//    GlobalDocID global = 3;
//    cout << global;                      <-- Prints 3 to stdout.
//
//    for (GlobalDocID i(0); i < global; ++i) {
//      cout << i;
//    }                                    <-- Print(ln)s 0 1 2 to stdout
//
//    DEFINE_INT_TYPE(LocalDocID, int64);
//    LocalDocID local;
//    cout << local;                       <-- Prints 0 to stdout it default
//                                             initializes the value to 0.
//
//    local = 5;
//    local *= 2;
//    LocalDocID l(local);
//    cout << l + local;                   <-- Prints 20 to stdout.
//
//    GenericSearchRequest request;
//    request.set_doc_id(global.value());  <-- Uses value() to extract the value
//                                             from the IntType 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 IntType class.  Keep in mind that the primary reason for the IntType
// class is to prevent *accidental* mingling of similar logical integer types --
// and not type casting from one type to another.
//
//  DEFINE_INT_TYPE(GlobalDocID, int64);
//  DEFINE_INT_TYPE(LocalDocID, int64);
//  GlobalDocID global;
//  LocalDocID local;
//
//  global = local.value();                       <-- Compiles fine.
//
//  void GetGlobalDoc(GlobalDocID global) { ...
//  GetGlobalDoc(local.value());                  <-- Compiles fine.
//
//  void GetGlobalDoc(int64 global) { ...
//  GetGlobalDoc(local.value());                  <-- Compiles fine.

#ifndef TENSORFLOW_LIB_GTL_INT_TYPE_H_
#define TENSORFLOW_LIB_GTL_INT_TYPE_H_

#include <stddef.h>
#include <functional>
#include <iosfwd>
#include <ostream>  // NOLINT
#include <unordered_map>

#include "tensorflow/core/platform/port.h"

namespace tensorflow {
namespace gtl {

template <typename IntTypeName, typename _ValueType>
class IntType;

// Defines the IntType 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 TF_LIB_GTL_DEFINE_INT_TYPE(int_type_name, value_type)          \
  struct int_type_name##_tag_ {};                                      \
  typedef ::tensorflow::gtl::IntType<int_type_name##_tag_, value_type> \
      int_type_name;

// Holds an integer value (of type ValueType) and behaves as a ValueType by
// exposing assignment, unary, comparison, and arithmetic operators.
//
// The template parameter IntTypeName defines the name for the int type and must
// be unique within a binary (the convenient DEFINE_INT_TYPE macro at the end of
// the file generates a unique IntTypeName).  The parameter ValueType defines
// the integer type value (see supported list above).
//
// This class is NOT thread-safe.
template <typename IntTypeName, typename _ValueType>
class IntType {
 public:
  typedef _ValueType ValueType;                      // for non-member operators
  typedef IntType<IntTypeName, ValueType> ThisType;  // Syntactic sugar.

  // Note that this may change from time to time without notice.
  struct Hasher {
    size_t operator()(const IntType& arg) const {
      return static_cast<size_t>(arg.value());
    }
  };

 public:
  // Default c'tor initializing value_ to 0.
  constexpr IntType() : value_(0) {}
  // C'tor explicitly initializing from a ValueType.
  constexpr explicit IntType(ValueType value) : value_(value) {}

  // IntType 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_);
  }

  // -- 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 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;                                    \
  }
  INT_TYPE_ASSIGNMENT_OP(+= );
  INT_TYPE_ASSIGNMENT_OP(-= );
  INT_TYPE_ASSIGNMENT_OP(*= );
  INT_TYPE_ASSIGNMENT_OP(/= );
  INT_TYPE_ASSIGNMENT_OP(<<= );  // NOLINT
  INT_TYPE_ASSIGNMENT_OP(>>= );  // NOLINT
  INT_TYPE_ASSIGNMENT_OP(%= );
#undef INT_TYPE_ASSIGNMENT_OP

  ThisType& operator=(ValueType arg_value) {
    value_ = arg_value;
    return *this;
  }

 private:
  // The integer value of type ValueType.
  ValueType value_;

  static_assert(std::is_integral<ValueType>::value, "invalid integer type");
} TF_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 IntTypeName, typename ValueType>
std::ostream& operator<<(std::ostream& os,  // NOLINT
                         IntType<IntTypeName, ValueType> arg) {
  return os << arg.value();
}

// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
// We support only the +, -, *, and / operators with the same IntType 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 IntType * IntType and IntType / IntType,
// it is probably non-sensical from a dimensionality analysis perspective.
#define INT_TYPE_ARITHMETIC_OP(op)                                        \
  template <typename IntTypeName, typename ValueType>                     \
  static inline constexpr IntType<IntTypeName, ValueType> operator op(    \
      IntType<IntTypeName, ValueType> id_1,                               \
      IntType<IntTypeName, ValueType> id_2) {                             \
    return IntType<IntTypeName, ValueType>(id_1.value() op id_2.value()); \
  }                                                                       \
  template <typename IntTypeName, typename ValueType>                     \
  static inline constexpr IntType<IntTypeName, ValueType> operator op(    \
      IntType<IntTypeName, ValueType> id,                                 \
      typename IntType<IntTypeName, ValueType>::ValueType arg_val) {      \
    return IntType<IntTypeName, ValueType>(id.value() op arg_val);        \
  }                                                                       \
  template <typename IntTypeName, typename ValueType>                     \
  static inline constexpr IntType<IntTypeName, ValueType> operator op(    \
      typename IntType<IntTypeName, ValueType>::ValueType arg_val,        \
      IntType<IntTypeName, ValueType> id) {                               \
    return IntType<IntTypeName, ValueType>(arg_val op id.value());        \
  }
INT_TYPE_ARITHMETIC_OP(+);
INT_TYPE_ARITHMETIC_OP(-);
INT_TYPE_ARITHMETIC_OP(*);
INT_TYPE_ARITHMETIC_OP(/ );
INT_TYPE_ARITHMETIC_OP(<< );  // NOLINT
INT_TYPE_ARITHMETIC_OP(>> );  // NOLINT
INT_TYPE_ARITHMETIC_OP(% );
#undef INT_TYPE_ARITHMETIC_OP

// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
// Static inline comparison operators.  We allow all comparison operators among
// the following types (OP \in [==, !=, <, <=, >, >=]:
//   IntType<IntTypeName, ValueType> OP IntType<IntTypeName, ValueType>
//   IntType<IntTypeName, ValueType> OP ValueType
//   ValueType OP IntType<IntTypeName, ValueType>
#define INT_TYPE_COMPARISON_OP(op)                               \
  template <typename IntTypeName, typename ValueType>            \
  static inline constexpr bool operator op(                      \
      IntType<IntTypeName, ValueType> id_1,                      \
      IntType<IntTypeName, ValueType> id_2) {                    \
    return id_1.value() op id_2.value();                         \
  }                                                              \
  template <typename IntTypeName, typename ValueType>            \
  static inline constexpr bool operator op(                      \
      IntType<IntTypeName, ValueType> id,                        \
      typename IntType<IntTypeName, ValueType>::ValueType val) { \
    return id.value() op val;                                    \
  }                                                              \
  template <typename IntTypeName, typename ValueType>            \
  static inline constexpr bool operator op(                      \
      typename IntType<IntTypeName, ValueType>::ValueType val,   \
      IntType<IntTypeName, ValueType> id) {                      \
    return val op id.value();                                    \
  }
INT_TYPE_COMPARISON_OP(== );  // NOLINT
INT_TYPE_COMPARISON_OP(!= );  // NOLINT
INT_TYPE_COMPARISON_OP(< );   // NOLINT
INT_TYPE_COMPARISON_OP(<= );  // NOLINT
INT_TYPE_COMPARISON_OP(> );   // NOLINT
INT_TYPE_COMPARISON_OP(>= );  // NOLINT
#undef INT_TYPE_COMPARISON_OP

}  // namespace gtl
}  // namespace tensorflow

#endif  // TENSORFLOW_LIB_GTL_INT_TYPE_H_