// #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 (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() method that serves as syntactic sugar // for static_cast(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 #include #include #include // NOLINT #include #include "tensorflow/core/platform/port.h" namespace tensorflow { namespace gtl { template 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; // 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 class IntType { public: typedef _ValueType ValueType; // for non-member operators typedef IntType 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(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(var.value()); constexpr ValueType value() const { return value_; } template constexpr ValType value() const { return static_cast(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::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 std::ostream& operator<<(std::ostream& os, // NOLINT IntType 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 \ static inline constexpr IntType operator op( \ IntType id_1, \ IntType id_2) { \ return IntType(id_1.value() op id_2.value()); \ } \ template \ static inline constexpr IntType operator op( \ IntType id, \ typename IntType::ValueType arg_val) { \ return IntType(id.value() op arg_val); \ } \ template \ static inline constexpr IntType operator op( \ typename IntType::ValueType arg_val, \ IntType id) { \ return IntType(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 OP IntType // IntType OP ValueType // ValueType OP IntType #define INT_TYPE_COMPARISON_OP(op) \ template \ static inline constexpr bool operator op( \ IntType id_1, \ IntType id_2) { \ return id_1.value() op id_2.value(); \ } \ template \ static inline constexpr bool operator op( \ IntType id, \ typename IntType::ValueType val) { \ return id.value() op val; \ } \ template \ static inline constexpr bool operator op( \ typename IntType::ValueType val, \ IntType 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_