// Copyright 2019 The MediaPipe Authors. // // 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 almost all of the same functionality // as native integer types, but which prevents assignment, construction, and // other operations from other integer-like types. In other words, you cannot // assign from raw integer types or other StrongInt<> types, nor can you do // most arithmetic or logical operations. This provides a simple form of // dimensionality in that you can add two instances of StrongInt, producing // a StrongInt, but you can not add a StrongInt and a raw T nor can you // add a StrongInt and a StrongInt. Details on supported operations are // below. // // In addition to type strength, StrongInt provides a way to inject (optional) // validation of the various operations. This allows you to define StrongInt // types that check for overflow conditions and react in standard or custom // ways. // // A StrongInt with a NullStrongIntValidator should compile away to a raw T // in optimized mode. What this means is that the generated assembly for: // // int64 foo = 123; // int64 bar = 456; // int64 baz = foo + bar; // constexpr int64 fubar = 789; // // ...should be identical to the generated assembly for: // // DEFINE_STRONG_INT_TYPE(MyStrongInt, int64); // MyStrongInt foo(123); // MyStrongInt bar(456); // MyStrongInt baz = foo + bar; // constexpr MyStrongInt fubar(789); // // Since the methods are all inline and non-virtual and the class has just // one data member, the compiler can erase the StrongInt class entirely in its // code-generation phase. This also means that you can pass StrongInt // around by value just as you would a raw T. // // It is important to note that StrongInt does NOT generate compile time // warnings or errors for overflows on implicit constant conversions. // // Usage: // StrongInt // // Creates a new StrongInt instance directly. // // TagType: The unique type which discriminates this StrongInt from // other StrongInt types. // NativeType: The primitive integral type this StrongInt will hold, as // defined by std::is_integral (see ). // ValidatorType: The type of validation used by this StrongInt type. A // few pre-built validator types are provided here, but the caller can // define any custom validator they desire. // // Supported operations: // StrongInt = StrongInt // !StrongInt => bool // ~StrongInt => StrongInt // -StrongInt => StrongInt // +StrongInt => StrongInt // ++StrongInt => StrongInt // StrongInt++ => StrongInt // --StrongInt => StrongInt // StrongInt-- => StrongInt // StrongInt + StrongInt => StrongInt // StrongInt - StrongInt => StrongInt // StrongInt * (numeric type) => StrongInt // StrongInt / (numeric type) => StrongInt // StrongInt % (numeric type) => StrongInt // StrongInt << (numeric type) => StrongInt // StrongInt >> (numeric type) => StrongInt // StrongInt & StrongInt => StrongInt // StrongInt | StrongInt => StrongInt // StrongInt ^ StrongInt => StrongInt // // For binary operations, the equivalent op-equal (eg += vs. +) operations are // also supported. Other operator combinations should cause compile-time // errors. // // Validators: // NullStrongIntValidator: Do no validation. This should be entirely // optimized away by the compiler. #ifndef MEDIAPIPE_DEPS_STRONG_INT_H_ #define MEDIAPIPE_DEPS_STRONG_INT_H_ #include #include #include #include #include "absl/base/macros.h" #include "mediapipe/framework/port/integral_types.h" #include "mediapipe/framework/port/port.h" namespace mediapipe { namespace intops { // Define the validators which can be plugged-in to make StrongInt resilient to // things like overflows. This is a do-nothing implementation of the // compile-time interface. // // NOTE: For all validation functions that operate on an existing StrongInt, // the type argument 'T' *must* be StrongInt::ValueType (the int type being // strengthened). struct NullStrongIntValidator { // Verify initialization of StrongInt from arg, type U. // // Note that this templated default implementation has an arbitrary bool // return value for the sole purpose of conforming to c++11 constexpr. // // Custom validator implementations can choose to return void or use a similar // return value constexpr construct if constexpr initialization is desirable. // // The StrongInt class does not care about or use the returned value. Any // returned value is solely there to allow the constexpr declaration; custom // validators can only fail / abort when detecting an invalid value. // // For example, other than the constexpr behavior, the below 2 custom // validator implementations are logically equivalent: // // template // static void ValidateInit(U arg) { // if (arg < 0) LOG(FATAL) << "arg < 0"; // } // // template // static constexpr bool ValidateInit(U arg) { // return (arg < 0) ? (LOG(FATAL) << "arg < 0", false) : false; // } // // A constexpr ValidateInit implementation has the added advantage that the // validation can take place (fail) at compile time. template static constexpr bool ValidateInit(U arg) { return true; } // Verify -value. template static void ValidateNegate(T value) { /* do nothing */ } // Verify ~value; template static void ValidateBitNot(T value) { /* do nothing */ } // Verify lhs + rhs. template static void ValidateAdd(T lhs, T rhs) { /* do nothing */ } // Verify lhs - rhs. template static void ValidateSubtract(T lhs, T rhs) { /* do nothing */ } // Verify lhs * rhs. template static void ValidateMultiply(T lhs, U rhs) { /* do nothing */ } // Verify lhs / rhs. template static void ValidateDivide(T lhs, U rhs) { /* do nothing */ } // Verify lhs % rhs. template static void ValidateModulo(T lhs, U rhs) { /* do nothing */ } // Verify lhs << rhs. template static void ValidateLeftShift(T lhs, int64 rhs) { /* do nothing */ } // Verify lhs >> rhs. template static void ValidateRightShift(T lhs, int64 rhs) { /* do nothing */ } // Verify lhs & rhs. template static void ValidateBitAnd(T lhs, T rhs) { /* do nothing */ } // Verify lhs | rhs. template static void ValidateBitOr(T lhs, T rhs) { /* do nothing */ } // Verify lhs ^ rhs. template static void ValidateBitXor(T lhs, T rhs) { /* do nothing */ } }; // Holds an integer value (of type NativeType) and behaves as a NativeType by // exposing assignment, unary, comparison, and arithmetic operators. // // This class is NOT thread-safe. template class StrongInt { public: typedef NativeType ValueType; // Default value initialization. constexpr StrongInt() : value_((ValidatorType::template ValidateInit(NativeType()), NativeType())) {} // Explicit initialization from another StrongInt type that has an // implementation of: // // ToType StrongIntConvert(FromType source, ToType*); // // This uses Argument Dependent Lookup (ADL) to find which function to // call. // // Example: Assume you have two StrongInt types. // // DEFINE_STRONG_INT_TYPE(Bytes, int64); // DEFINE_STRONG_INT_TYPE(Megabytes, int64); // // If you want to be able to (explicitly) construct an instance of Bytes from // an instance of Megabytes, simply define a converter function in the same // namespace as either Bytes or Megabytes (or both): // // Megabytes StrongIntConvert(Bytes arg, Megabytes* /* unused */) { // return Megabytes((arg >> 20).value()); // }; // // The second argument is needed to differentiate conversions, and it always // passed as NULL. template explicit StrongInt( StrongInt arg) { // We have to pass both the "from" type and the "to" type as args for the // conversions to be differentiated. The converter can not be a template // because explicit template call syntax defeats ADL. StrongInt *dummy = NULL; StrongInt converted = StrongIntConvert(arg, dummy); value_ = converted.value(); } // Explicit initialization from a numeric primitive. template ::value>::type> explicit constexpr StrongInt(T init_value) : value_((ValidatorType::template ValidateInit(init_value), static_cast(init_value))) {} // Use the default copy constructor, assignment, and destructor. // Accesses the raw value. constexpr ValueType value() const { return value_; } // Accesses the raw value, with cast. // Primarily for compatibility with int-type.h template constexpr ValType value() const { return static_cast(value_); } // Metadata functions. static ValueType Max() { return std::numeric_limits::max(); } static ValueType Min() { return std::numeric_limits::min(); } // Unary operators. bool operator!() const { return value_ == 0; } const StrongInt operator+() const { return StrongInt(value_); } const StrongInt operator-() const { ValidatorType::template ValidateNegate(value_); return StrongInt(-value_); } const StrongInt operator~() const { ValidatorType::template ValidateBitNot(value_); return StrongInt(ValueType(~value_)); } // Increment and decrement operators. StrongInt &operator++() { // ++x ValidatorType::template ValidateAdd(value_, ValueType(1)); ++value_; return *this; } const StrongInt operator++(int postfix_flag) { // x++ ValidatorType::template ValidateAdd(value_, ValueType(1)); StrongInt temp(*this); ++value_; return temp; } StrongInt &operator--() { // --x ValidatorType::template ValidateSubtract(value_, ValueType(1)); --value_; return *this; } const StrongInt operator--(int postfix_flag) { // x-- ValidatorType::template ValidateSubtract(value_, ValueType(1)); StrongInt temp(*this); --value_; return temp; } // Action-Assignment operators. StrongInt &operator+=(StrongInt arg) { ValidatorType::template ValidateAdd(value_, arg.value()); value_ += arg.value(); return *this; } StrongInt &operator-=(StrongInt arg) { ValidatorType::template ValidateSubtract(value_, arg.value()); value_ -= arg.value(); return *this; } template StrongInt &operator*=(ArgType arg) { ValidatorType::template ValidateMultiply(value_, arg); value_ *= arg; return *this; } template StrongInt &operator/=(ArgType arg) { ValidatorType::template ValidateDivide(value_, arg); value_ /= arg; return *this; } template StrongInt &operator%=(ArgType arg) { ValidatorType::template ValidateModulo(value_, arg); value_ %= arg; return *this; } StrongInt &operator<<=(int64 arg) { // NOLINT(whitespace/operators) ValidatorType::template ValidateLeftShift(value_, arg); value_ <<= arg; return *this; } StrongInt &operator>>=(int64 arg) { // NOLINT(whitespace/operators) ValidatorType::template ValidateRightShift(value_, arg); value_ >>= arg; return *this; } StrongInt &operator&=(StrongInt arg) { ValidatorType::template ValidateBitAnd(value_, arg.value()); value_ &= arg.value(); return *this; } StrongInt &operator|=(StrongInt arg) { ValidatorType::template ValidateBitOr(value_, arg.value()); value_ |= arg.value(); return *this; } StrongInt &operator^=(StrongInt arg) { ValidatorType::template ValidateBitXor(value_, 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 for strong int"); }; // Provide the << operator, primarily for logging purposes. template std::ostream &operator<<(std::ostream &os, StrongInt arg) { return os << arg.value(); } // Provide the << operator, primarily for logging purposes. Specialized for int8 // so that an integer and not a character is printed. template std::ostream &operator<<(std::ostream &os, StrongInt arg) { return os << static_cast(arg.value()); } // Provide the << operator, primarily for logging purposes. Specialized for // uint8 so that an integer and not a character is printed. template std::ostream &operator<<(std::ostream &os, StrongInt arg) { return os << static_cast(arg.value()); } // Define operators that take two StrongInt arguments. These operators are // defined in terms of their op-equal member function cousins. #define STRONG_INT_VS_STRONG_INT_BINARY_OP(op) \ template \ inline StrongInt operator op( \ StrongInt lhs, \ StrongInt rhs) { \ lhs op## = rhs; \ return lhs; \ } STRONG_INT_VS_STRONG_INT_BINARY_OP(+); STRONG_INT_VS_STRONG_INT_BINARY_OP(-); STRONG_INT_VS_STRONG_INT_BINARY_OP(&); STRONG_INT_VS_STRONG_INT_BINARY_OP(|); STRONG_INT_VS_STRONG_INT_BINARY_OP(^); #undef STRONG_INT_VS_STRONG_INT_BINARY_OP // Define operators that take one StrongInt and one native integer argument. // These operators are defined in terms of their op-equal member function // cousins, mostly. #define STRONG_INT_VS_NUMERIC_BINARY_OP(op) \ template \ inline StrongInt operator op( \ StrongInt lhs, NumType rhs) { \ lhs op## = rhs; \ return lhs; \ } // This is used for commutative operators between one StrongInt and one native // integer argument. That is a long way of saying "multiplication". #define NUMERIC_VS_STRONG_INT_BINARY_OP(op) \ template \ inline StrongInt operator op( \ NumType lhs, StrongInt rhs) { \ rhs op## = lhs; \ return rhs; \ } STRONG_INT_VS_NUMERIC_BINARY_OP(*); NUMERIC_VS_STRONG_INT_BINARY_OP(*); STRONG_INT_VS_NUMERIC_BINARY_OP(/); STRONG_INT_VS_NUMERIC_BINARY_OP(%); STRONG_INT_VS_NUMERIC_BINARY_OP(<<); // NOLINT(whitespace/operators) STRONG_INT_VS_NUMERIC_BINARY_OP(>>); // NOLINT(whitespace/operators) #undef STRONG_INT_VS_NUMERIC_BINARY_OP #undef NUMERIC_VS_STRONG_INT_BINARY_OP // Define comparison operators. We allow all comparison operators. #define STRONG_INT_COMPARISON_OP(op) \ template \ inline bool operator op(StrongInt lhs, \ StrongInt rhs) { \ return lhs.value() op rhs.value(); \ } STRONG_INT_COMPARISON_OP(==); // NOLINT(whitespace/operators) STRONG_INT_COMPARISON_OP(!=); // NOLINT(whitespace/operators) STRONG_INT_COMPARISON_OP(<); // NOLINT(whitespace/operators) STRONG_INT_COMPARISON_OP(<=); // NOLINT(whitespace/operators) STRONG_INT_COMPARISON_OP(>); // NOLINT(whitespace/operators) STRONG_INT_COMPARISON_OP(>=); // NOLINT(whitespace/operators) #undef STRONG_INT_COMPARISON_OP } // namespace intops } // namespace mediapipe #endif // MEDIAPIPE_DEPS_STRONG_INT_H_