Files
ortools-clone/ortools/util/strong_integers.h
Corentin Le Molgat b4b226801b update include guards
2025-11-05 11:54:02 +01:00

485 lines
20 KiB
C++

// Copyright 2010-2025 Google LLC
// 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.
// Generates strongly typed integer types.
//
// StrongIndex is a simple template class mechanism for defining "logical"
// index-like class types that support some of the same functionalities
// as int, but which prevent assignment, construction, and
// other operations from other similar integer-like types. Essentially, the
// template class StrongIndex<StrongIndexName> has the additional
// property that it cannot be assigned to or constructed from another
// StrongIndex with a different StrongIndexName.
//
// Usage
// DEFINE_STRONG_INDEX_TYPE(name);
// where name is the desired (unique) name for the "logical" index type.
//
// StrongInt64 is a more general strong integer class based on int64_t.
// It has the same general type safeness, and supports more integer operators.
//
// Usage
// DEFINE_STRONG_INT64_TYPE(name);
// where name is the desired (unique) name for the "logical" int64_t type.
//
// SUPPORTED OPERATIONS --------------------------------------------------------
//
// The StrongIndex type is limited and only supports following operators are
// supported: unary: ++ (both prefix and postfix), comparison: ==, !=, <, <=, >,
// >=; assignment: =, +=, -=,; stream: <<. Each operator allows the same
// StrongIndexName and the int to be used on both left- and right-hand sides.
//
// The StrongInt64 type supports all integer operators across StrongInt64
// with the same StrongIntegerName and int64_t.
//
// Both support an accessor value() returning the stored value.
//
// The classes also define hash functors that allows the strong types to be used
// as key to hashable containers.
#ifndef ORTOOLS_UTIL_STRONG_INTEGERS_H_
#define ORTOOLS_UTIL_STRONG_INTEGERS_H_
#include <stddef.h>
#include <cstdint>
#include <iosfwd>
#include <limits>
#include <ostream> // NOLINT
#include "absl/base/attributes.h"
#include "absl/base/port.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
namespace operations_research {
// Defines the StrongIndex and typedefs it to index_type_name.
//
// Note: The struct index_type_name ## _index_tag_ trickery is needed to ensure
// that a new type is created per index_type_name.
#define DEFINE_STRONG_INDEX_TYPE(index_type_name) \
struct index_type_name##_index_tag_ { \
static constexpr absl::string_view TypeName() { return #index_type_name; } \
}; \
typedef ::operations_research::StrongIndex<index_type_name##_index_tag_> \
index_type_name;
// Defines the StrongInt64 and typedefs it to integer_type_name.
//
// Note: The struct integer_type_name ## _integer_tag_ trickery is needed to
// ensure that a new type is created per integer_type_name.
#define DEFINE_STRONG_INT64_TYPE(integer_type_name) \
struct integer_type_name##_integer_tag_ { \
static constexpr absl::string_view TypeName() { \
return #integer_type_name; \
} \
}; \
typedef ::operations_research::StrongInt64<integer_type_name##_integer_tag_> \
integer_type_name;
// ----------- Implementation ------------
// Note: we need two classes as it is the only way to have different set of
// operators on each class.
// Note: We use to class to easily define a different set of operators for the
// index and int64_t type.
#define STRONG_ASSIGNMENT_OP(StrongClass, IntType, op) \
ThisType& operator op(const ThisType& arg_value) { \
value_ op arg_value.value(); \
return *this; \
} \
ThisType& operator op(IntType arg_value) { \
value_ op arg_value; \
return *this; \
}
#define INCREMENT_AND_DECREMENT_OPERATORS \
ThisType& operator++() { \
++value_; \
return *this; \
} \
const ThisType operator++(int) { \
ThisType temp(*this); \
++value_; \
return temp; \
} \
ThisType& operator--() { \
--value_; \
return *this; \
} \
const ThisType operator--(int) { \
ThisType temp(*this); \
--value_; \
return temp; \
}
// Holds an int value and behaves as an int by exposing assignment,
// unary, comparison, and arithmetic operators.
//
// The template parameter StrongIndexName defines the name for the int type and
// must be unique within a binary (the convenient DEFINE_STRONG_INDEX_TYPE macro
// at the start of the file generates a unique StrongIndexName).
//
// This class is NOT thread-safe.
template <typename StrongIndexName>
class StrongIndex {
public:
typedef int ValueType; // Needed for StrongVector.
typedef StrongIndex<StrongIndexName> ThisType; // Syntactic sugar.
static constexpr absl::string_view TypeName() {
return StrongIndexName::TypeName();
}
struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher {
size_t operator()(const StrongIndex& x) const {
return static_cast<size_t>(x.value());
}
};
constexpr StrongIndex() : value_(0) {}
explicit constexpr StrongIndex(int value) : value_(value) {}
StrongIndex& operator=(int arg_value) {
value_ = arg_value;
return *this;
}
// The class provides a value() accessor returning the stored int value_
// as well as a templatized accessor that is just a syntactic sugar for
// static_cast<T>(var.value());
constexpr int value() const { return value_; }
template <typename ValType> // Needed for StrongVector.
constexpr ValType value() const {
return static_cast<ValType>(value_);
}
constexpr ThisType operator+() const { return ThisType(value_); }
constexpr ThisType operator-() const { return ThisType(-value_); }
INCREMENT_AND_DECREMENT_OPERATORS;
STRONG_ASSIGNMENT_OP(StrongIndex, int, +=);
STRONG_ASSIGNMENT_OP(StrongIndex, int, -=);
private:
int value_;
};
// Holds an int64_t value and behaves as an int64_t by exposing assignment,
// unary, comparison, and arithmetic operators.
//
// The template parameter StrongIntegerName defines the name for the int type
// and must be unique within a binary (the convenient DEFINE_STRONG_INTEGER_TYPE
// macro at the start of the file generates a unique StrongIntegerName).
//
// This class is NOT thread-safe.
template <typename StrongIntegerName>
class StrongInt64 {
public:
typedef int64_t ValueType; // Needed for StrongVector.
typedef StrongInt64<StrongIntegerName> ThisType; // Syntactic sugar.
static constexpr absl::string_view TypeName() {
return StrongIntegerName::TypeName();
}
struct ABSL_DEPRECATED("Use absl::Hash instead") Hasher {
size_t operator()(const StrongInt64& x) const {
return static_cast<size_t>(x.value());
}
};
constexpr StrongInt64() : value_(0) {}
// NOLINTBEGIN(google-explicit-constructor)
constexpr StrongInt64(int64_t value) : value_(value) {}
// NOLINTEND(google-explicit-constructor)
StrongInt64& operator=(int64_t arg_value) {
value_ = arg_value;
return *this;
}
constexpr int64_t value() const { return value_; }
int64_t* mutable_value() { return &value_; }
template <typename ValType> // Needed for StrongVector.
constexpr ValType value() const {
return static_cast<ValType>(value_);
}
INCREMENT_AND_DECREMENT_OPERATORS;
constexpr ThisType operator+() const { return ThisType(value_); }
constexpr ThisType operator-() const { return ThisType(-value_); }
constexpr ThisType operator~() const { return ThisType(~value_); }
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, +=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, -=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, *=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, /=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, <<=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, >>=);
STRONG_ASSIGNMENT_OP(StrongInt64, int64_t, %=);
private:
int64_t value_;
};
#undef STRONG_ASSIGNMENT_OP
#undef INCREMENT_AND_DECREMENT_OPERATORS
// -- NON-MEMBER STREAM OPERATORS ----------------------------------------------
// We provide the << operator, primarily for logging purposes. Currently, there
// seems to be no need for an >> operator.
template <typename StrongIndexName>
std::ostream& operator<<(std::ostream& os, // NOLINT
StrongIndex<StrongIndexName> arg) {
return os << arg.value();
}
template <typename Sink, typename... T>
void AbslStringify(Sink& sink, StrongIndex<T...> arg) {
absl::Format(&sink, "%v", arg.value());
}
template <typename StrongIntegerName>
std::ostream& operator<<(std::ostream& os, // NOLINT
StrongInt64<StrongIntegerName> arg) {
return os << arg.value();
}
template <typename Sink, typename... T>
void AbslStringify(Sink& sink, StrongInt64<T...> arg) {
absl::Format(&sink, "%v", arg.value());
}
// -- NON-MEMBER ARITHMETIC OPERATORS ------------------------------------------
#define STRONG_TYPE_ARITHMETIC_OP(StrongType, IntType, op) \
template <typename StrongName> \
constexpr StrongType<StrongName> operator op(StrongType<StrongName> id_1, \
StrongType<StrongName> id_2) { \
return StrongType<StrongName>(id_1.value() op id_2.value()); \
} \
template <typename StrongName> \
constexpr StrongType<StrongName> operator op(StrongType<StrongName> id, \
IntType arg_val) { \
return StrongType<StrongName>(id.value() op arg_val); \
} \
template <typename StrongName> \
constexpr StrongType<StrongName> operator op(IntType arg_val, \
StrongType<StrongName> id) { \
return StrongType<StrongName>(arg_val op id.value()); \
}
STRONG_TYPE_ARITHMETIC_OP(StrongIndex, int, +);
STRONG_TYPE_ARITHMETIC_OP(StrongIndex, int, -);
STRONG_TYPE_ARITHMETIC_OP(StrongIndex, int, *);
STRONG_TYPE_ARITHMETIC_OP(StrongIndex, int, %);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, +);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, -);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, *);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, /);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, <<);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, >>);
STRONG_TYPE_ARITHMETIC_OP(StrongInt64, int64_t, %);
#undef STRONG_TYPE_ARITHMETIC_OP
// -- NON-MEMBER COMPARISON OPERATORS ------------------------------------------
#define STRONG_TYPE_COMPARISON_OP(StrongType, IntType, op) \
template <typename StrongName> \
static inline constexpr bool operator op(StrongType<StrongName> id_1, \
StrongType<StrongName> id_2) { \
return id_1.value() op id_2.value(); \
} \
template <typename StrongName> \
static inline constexpr bool operator op(StrongType<StrongName> id, \
IntType val) { \
return id.value() op val; \
} \
template <typename StrongName> \
static inline constexpr bool operator op(IntType val, \
StrongType<StrongName> id) { \
return val op id.value(); \
}
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, ==); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, !=); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, <); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, <=); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, >); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongIndex, int, >=); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, ==); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, !=); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, <); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, <=); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, >); // NOLINT
STRONG_TYPE_COMPARISON_OP(StrongInt64, int64_t, >=); // NOLINT
#undef STRONG_TYPE_COMPARISON_OP
// -- ABSL HASHING SUPPORT -----------------------------------------------------
template <typename StrongIndexName, typename H>
H AbslHashValue(H h, const StrongIndex<StrongIndexName>& i) {
return H::combine(std::move(h), i.value());
}
template <typename StrongIntegerName, typename H>
H AbslHashValue(H h, const StrongInt64<StrongIntegerName>& i) {
return H::combine(std::move(h), i.value());
}
} // namespace operations_research
// -- STD HASHING SUPPORT -----------------------------------------------------
namespace std {
template <typename Tag>
struct hash<operations_research::StrongIndex<Tag>>
: ::operations_research::StrongIndex<Tag>::Hasher {};
template <typename Tag>
struct hash<operations_research::StrongInt64<Tag>>
: ::operations_research::StrongInt64<Tag>::Hasher {};
} // namespace std
// -- STD NUMERIC_LIMITS SUPPORT ----------------------------------------------
namespace std {
template <typename Tag>
struct numeric_limits<operations_research::StrongIndex<Tag>> {
private:
using StrongIntT = operations_research::StrongIndex<Tag>;
using NativeTypeT = typename operations_research::StrongIndex<Tag>::ValueType;
public:
// NOLINTBEGIN(google3-readability-class-member-naming)
static constexpr bool is_specialized = true;
static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
static constexpr bool has_infinity =
numeric_limits<NativeTypeT>::has_infinity;
static constexpr bool has_quiet_NaN =
numeric_limits<NativeTypeT>::has_quiet_NaN;
static constexpr bool has_signaling_NaN =
numeric_limits<NativeTypeT>::has_signaling_NaN;
static constexpr float_denorm_style has_denorm =
numeric_limits<NativeTypeT>::has_denorm;
static constexpr bool has_denorm_loss =
numeric_limits<NativeTypeT>::has_denorm_loss;
static constexpr float_round_style round_style =
numeric_limits<NativeTypeT>::round_style;
static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
static constexpr int digits = numeric_limits<NativeTypeT>::digits;
static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
static constexpr int radix = numeric_limits<NativeTypeT>::radix;
static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
static constexpr int min_exponent10 =
numeric_limits<NativeTypeT>::min_exponent10;
static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
static constexpr int max_exponent10 =
numeric_limits<NativeTypeT>::max_exponent10;
static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
static constexpr bool tinyness_before =
numeric_limits<NativeTypeT>::tinyness_before;
// NOLINTEND(google3-readability-class-member-naming)
static constexpr StrongIntT(min)() {
return StrongIntT(numeric_limits<NativeTypeT>::min());
}
static constexpr StrongIntT lowest() {
return StrongIntT(numeric_limits<NativeTypeT>::lowest());
}
static constexpr StrongIntT(max)() {
return StrongIntT(numeric_limits<NativeTypeT>::max());
}
static constexpr StrongIntT epsilon() { return StrongIntT(); }
static constexpr StrongIntT round_error() { return StrongIntT(); }
static constexpr StrongIntT infinity() { return StrongIntT(); }
static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
static constexpr StrongIntT denorm_min() { return StrongIntT(); }
};
template <typename Tag>
struct numeric_limits<operations_research::StrongInt64<Tag>> {
private:
using StrongIntT = operations_research::StrongInt64<Tag>;
using NativeTypeT = typename operations_research::StrongInt64<Tag>::ValueType;
public:
// NOLINTBEGIN(google3-readability-class-member-naming)
static constexpr bool is_specialized = true;
static constexpr bool is_signed = numeric_limits<NativeTypeT>::is_signed;
static constexpr bool is_integer = numeric_limits<NativeTypeT>::is_integer;
static constexpr bool is_exact = numeric_limits<NativeTypeT>::is_exact;
static constexpr bool has_infinity =
numeric_limits<NativeTypeT>::has_infinity;
static constexpr bool has_quiet_NaN =
numeric_limits<NativeTypeT>::has_quiet_NaN;
static constexpr bool has_signaling_NaN =
numeric_limits<NativeTypeT>::has_signaling_NaN;
static constexpr float_denorm_style has_denorm =
numeric_limits<NativeTypeT>::has_denorm;
static constexpr bool has_denorm_loss =
numeric_limits<NativeTypeT>::has_denorm_loss;
static constexpr float_round_style round_style =
numeric_limits<NativeTypeT>::round_style;
static constexpr bool is_iec559 = numeric_limits<NativeTypeT>::is_iec559;
static constexpr bool is_bounded = numeric_limits<NativeTypeT>::is_bounded;
static constexpr bool is_modulo = numeric_limits<NativeTypeT>::is_modulo;
static constexpr int digits = numeric_limits<NativeTypeT>::digits;
static constexpr int digits10 = numeric_limits<NativeTypeT>::digits10;
static constexpr int max_digits10 = numeric_limits<NativeTypeT>::max_digits10;
static constexpr int radix = numeric_limits<NativeTypeT>::radix;
static constexpr int min_exponent = numeric_limits<NativeTypeT>::min_exponent;
static constexpr int min_exponent10 =
numeric_limits<NativeTypeT>::min_exponent10;
static constexpr int max_exponent = numeric_limits<NativeTypeT>::max_exponent;
static constexpr int max_exponent10 =
numeric_limits<NativeTypeT>::max_exponent10;
static constexpr bool traps = numeric_limits<NativeTypeT>::traps;
static constexpr bool tinyness_before =
numeric_limits<NativeTypeT>::tinyness_before;
// NOLINTEND(google3-readability-class-member-naming)
static constexpr StrongIntT(min)() {
return StrongIntT(numeric_limits<NativeTypeT>::min());
}
static constexpr StrongIntT lowest() {
return StrongIntT(numeric_limits<NativeTypeT>::lowest());
}
static constexpr StrongIntT(max)() {
return StrongIntT(numeric_limits<NativeTypeT>::max());
}
static constexpr StrongIntT epsilon() { return StrongIntT(); }
static constexpr StrongIntT round_error() { return StrongIntT(); }
static constexpr StrongIntT infinity() { return StrongIntT(); }
static constexpr StrongIntT quiet_NaN() { return StrongIntT(); }
static constexpr StrongIntT signaling_NaN() { return StrongIntT(); }
static constexpr StrongIntT denorm_min() { return StrongIntT(); }
};
} // namespace std
#endif // ORTOOLS_UTIL_STRONG_INTEGERS_H_