Flow 1.0.0
Flow project: Full implementation reference.
|
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from all other objects of the same class ever constructed, across all time from program start to end. More...
#include <uniq_id_holder.hpp>
Public Types | |
using | id_t = uint64_t |
Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to reach. More... | |
Public Member Functions | |
Unique_id_holder () | |
Thread-safely construct an ID whose value is different from any other object of this class, past or future. More... | |
Unique_id_holder (const Unique_id_holder &) | |
This copy constructor is identical in behavior to Unique_id_holder(), the default ctor. More... | |
id_t | unique_id () const |
Raw unique ID identifying this object as well as any object of a derived type. More... | |
const Unique_id_holder & | operator= (const Unique_id_holder &) const |
This assignment operator is a const no-op. More... | |
Static Public Member Functions | |
static id_t | create_unique_id () |
Short-hand for Unique_id_holder().unique_id() ; useful when all you want is the unique integer itself. More... | |
Private Attributes | |
const id_t | m_id |
The ID. Note its const ness alone forbids a classic assignment operator. More... | |
Static Private Attributes | |
static std::atomic< id_t > | s_last_id |
m_id value of the Unique_id_holder object last constructed. More... | |
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from all other objects of the same class ever constructed, across all time from program start to end.
To be clear, the uniqueness is not just for all existing objects at a given point in time (for which this
could simply be used instead) but across all time.
For obvious reasons, Unique_id_holder cannot have standard copy construction and assignment semantics. It was contemplated to simply make class noncopyable (forbidding copy construction). However, then no derived type could use auto-generated copy construction and/or operator=()
, making the deriving usage pattern (see below) considerably less concise for such types.
Therefore, it defines a (somewhat unusual) copy constructor: It is simply equal to the default constructor and simply generates a unique ID. The reason: a common pattern (see below) will involve type C
deriving from Unique_id_holder. If C
happens to use the auto-generated copy constructor implementation, Unique_id_holder's copy constructor will be invoked by this implementation. Since equal (by value) objects are still separate objects, the correct behavior is for the constructed object to gain a new ID.
The assignment operator operator=() similarly has unusual behavior: it does nothing, for similar reasons.
All operations safe for simultaneous execution on 2+ separate objects or on the same object. The ID accessor is a totally trivial accessor. Only construction requires any concurrent-access protection and internally uses an atomic
for good efficiency.
This can be used, in particular, to uniquely identify (over all time) objects of any type (across all types). The most concise usage pattern is probably via public (or possibly private) inheritance. Another is simply via composition (storage).
Update: A simplified pattern is now possible: If you just want the raw unique number itself and don't even want to keep Unique_id_holder objects around or refer to them, call the static create_unique_id() which returns a new raw id_t
integer. The above 2 usage patterns can be rewritten to pass around id_t
values. The "Example of outside code registering an object" sub-case is the one exception to that, as for that either store_in_table()
must continue to take a Unique_id_holder and not a raw integer, or I suppose m_uniq_id_holder
must be made public which some would not like.
Definition at line 106 of file uniq_id_holder.hpp.
using flow::util::Unique_id_holder::id_t = uint64_t |
Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to reach.
Definition at line 112 of file uniq_id_holder.hpp.
|
explicit |
Thread-safely construct an ID whose value is different from any other object of this class, past or future.
Definition at line 31 of file uniq_id_holder.cpp.
Referenced by create_unique_id().
|
explicit |
This copy constructor is identical in behavior to Unique_id_holder(), the default ctor.
Unique_id_holder doc header explains this behavior and why copy construction is not disallowed entirely.
Definition at line 41 of file uniq_id_holder.cpp.
|
static |
Short-hand for Unique_id_holder().unique_id()
; useful when all you want is the unique integer itself.
Definition at line 58 of file uniq_id_holder.cpp.
References Unique_id_holder().
Referenced by flow::util::schedule_task_from_now().
const Unique_id_holder & flow::util::Unique_id_holder::operator= | ( | const Unique_id_holder & | ) | const |
This assignment operator is a const
no-op.
Unique_id_holder doc header explains this behavior, and why assignment is not disallowed entirely.
*this
. Definition at line 47 of file uniq_id_holder.cpp.
Unique_id_holder::id_t flow::util::Unique_id_holder::unique_id | ( | ) | const |
Raw unique ID identifying this object as well as any object of a derived type.
Definition at line 53 of file uniq_id_holder.cpp.
References m_id.
Referenced by flow::log::Thread_local_string_appender::get_this_thread_string_appender().
|
private |
The ID. Note its const
ness alone forbids a classic assignment operator.
Definition at line 154 of file uniq_id_holder.hpp.
Referenced by unique_id().
|
staticprivate |
m_id value of the Unique_id_holder object last constructed.
Using atomic
for concise thread safety.
I don't specify any memory ordering arguments when using this because of the simplicity of this implementation (a lone ++
). Be careful if the code increases in complexity though; any kind of thread coordination would probably mean one would have to think of such matters and switch away from just overloaded operator(s) in favor of explicitly-memory-order-specifying atomic<>
API calls.
Normally when an std
implementation is available along with a boost
one, I choose the latter, for the likely extra features and for consistency. I chose to do differently in the case of atomic
, because, firstly, this is widely recommended, and, secondly, because this is a low-level feature and "feels" like it might be particularly well coded for a given architecture's compiler. Not that the Boost guys aren't great.
Definition at line 172 of file uniq_id_holder.hpp.