A simple identifier class.
This class serves as an upgrade to the standard practice of passing int
s around as unique identifiers (or, as in this case, int64_t
s). In the common practice, a method that takes identifiers to different types of objects would have an interface like:
It is possible for a programmer to accidentally switch the two ids in an invocation. This mistake would still be syntactically correct; it will successfully compile but lead to inscrutable run-time errors. This identifier class provides the same speed and efficiency of passing int64_t
s, but enforces unique types and limits the valid operations, providing compile-time checking. The function would now look like:
and the compiler will catch instances where the order is reversed.
The identifier is a stripped down 64-bit int. Each uniquely declared identifier type has the following properties:
int64_t
value.int64_t
value gets written.While there is the concept of an invalid identifier, this only exists to facilitate use with STL containers that require default constructors. Using an invalid identifier is generally considered to be an error. In Debug build, attempts to compare, get the value of, or write an invalid identifier to a stream will throw an exception.
Functions that query for identifiers should not return invalid identifiers. We prefer the practice of returning std::optional<Identifier> instead.
It is the designed intent of this class, that ids derived from this class can be passed and returned by value. (Drake's typical calling convention requires passing input arguments by const reference, or by value when moved from. That convention does not apply to this class.)
The following alias will create a unique identifier type for class Foo
:
Examples of valid and invalid operations
The Identifier guarantees that id instances of different types can't be compared or combined. Efforts to do so will cause a compile-time failure. For example:
In principle, the identifier is related to the TypeSafeIndex. In some sense, both are "type-safe" int
s. They differ in their semantics. We can consider ints
, indices, and identifiers as a list of int
types with decreasing functionality.
int
and other indices of the same type. This behavior arises from the intention of having them serve as an index in an ordered set (e.g., std::vector
).Ultimately, indices can serve as identifiers (within the scope of the object they index into). Although, their mutability could make this a dangerous practice for a public API. Identifiers are more general in that they don't reflect an object's position in memory (hence the inability to transform to or compare with an int
). This decouples details of implementation from the idea of the object. Combined with its immutability, it would serve well as a element of a public API.
Tag | The name of the tag that uniquely segregates one instantiation from another. |
#include <drake/common/identifier.h>
Public Member Functions | |
Identifier () | |
Default constructor; the result is an invalid identifier. More... | |
int64_t | get_value () const |
Extracts the underlying representation from the identifier. More... | |
bool | is_valid () const |
Reports if the id is valid. More... | |
bool | operator== (Identifier other) const |
Compares one identifier with another of the same type for equality. More... | |
bool | operator!= (Identifier other) const |
Compares one identifier with another of the same type for inequality. More... | |
bool | operator< (Identifier other) const |
Compare two identifiers in order to define a total ordering among identifiers. More... | |
bool | is_same_as_valid_id (Identifier valid_id) const |
(Internal use only) Compares this possibly-invalid Identifier with one that is known to be valid and returns false if they don't match. More... | |
Implements CopyConstructible, CopyAssignable, MoveConstructible, MoveAssignable | |
Identifier (const Identifier &)=default | |
Identifier & | operator= (const Identifier &)=default |
Identifier (Identifier &&)=default | |
Identifier & | operator= (Identifier &&)=default |
Static Public Member Functions | |
static Identifier | get_new_id () |
Generates a new identifier for this id type. More... | |
Protected Member Functions | |
Identifier (int64_t val) | |
Friends | |
template<typename HashAlgorithm > | |
void | hash_append (HashAlgorithm &hasher, const Identifier &i) noexcept |
Implements the hash_append generic hashing concept. More... | |
template<typename H > | |
H | AbslHashValue (H state, const Identifier &id) |
Implements Abseil's hashing concept. More... | |
Related Functions | |
(Note that these are not member functions.) | |
template<typename Tag > | |
std::ostream & | operator<< (std::ostream &out, const Identifier< Tag > &id) |
Streaming output operator. More... | |
|
default |
|
default |
Identifier | ( | ) |
Default constructor; the result is an invalid identifier.
This only exists to satisfy demands of working with various container classes.
|
explicitprotected |
|
static |
Generates a new identifier for this id type.
This new identifier will be different from all previous identifiers created. This method does not make any guarantees about the values of ids from successive invocations. This method is guaranteed to be thread safe.
int64_t get_value | ( | ) | const |
Extracts the underlying representation from the identifier.
This is considered invalid for invalid ids and is strictly enforced in Debug builds.
bool is_same_as_valid_id | ( | Identifier< Tag > | valid_id | ) | const |
(Internal use only) Compares this possibly-invalid Identifier with one that is known to be valid and returns false
if they don't match.
It is an error if valid_id
is not actually valid, and that is strictly enforced in Debug builds. However, it is not an error if this
id is invalid; that results in a false
return. This method can be faster than testing separately for validity and equality.
bool is_valid | ( | ) | const |
Reports if the id is valid.
bool operator!= | ( | Identifier< Tag > | other | ) | const |
Compares one identifier with another of the same type for inequality.
This is considered invalid for invalid ids and is strictly enforced in Debug builds.
bool operator< | ( | Identifier< Tag > | other | ) | const |
Compare two identifiers in order to define a total ordering among identifiers.
This makes identifiers compatible with data structures which require total ordering (e.g., std::set).
|
default |
|
default |
bool operator== | ( | Identifier< Tag > | other | ) | const |
Compares one identifier with another of the same type for equality.
This is considered invalid for invalid ids and is strictly enforced in Debug builds.
|
friend |
Implements Abseil's hashing concept.
|
friend |
Implements the hash_append generic hashing concept.
And invalid id will successfully hash (in order to satisfy STL requirements), and it is up to the user to confirm it is valid before using it as a key (or other hashing application).
|
related |
Streaming output operator.
This is considered invalid for invalid ids and is strictly enforced in Debug builds.