Flow 3.0.0
Flow project: Full implementation reference.
uniq_id_holder.hpp
Go to the documentation of this file.
1/* Flow
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#pragma once
20
22#include <atomic>
23#include <type_traits>
24
25namespace flow::util
26{
27
28/**
29 * Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from all other
30 * objects of the same class ever constructed, across all time from program start to end. To be clear, the uniqueness
31 * is not just for all existing objects at a given point in time (for which `this` could simply be used instead) but
32 * across all time.
33 *
34 * ### Copying behavior ###
35 * For obvious reasons, Unique_id_holder cannot have standard copy construction and assignment semantics.
36 * It was contemplated to simply make class noncopyable (forbidding copy construction).
37 * However, then no derived type could use auto-generated copy construction and/or `operator=()`, making the
38 * deriving usage pattern (see below) considerably less concise for such types.
39 *
40 * Therefore, it defines a (somewhat unusual) copy constructor: It is simply equal to the default constructor and simply
41 * generates a unique ID. The reason: a common pattern (see below) will involve type `C` deriving from
42 * Unique_id_holder. If `C` happens to use the auto-generated copy constructor implementation, Unique_id_holder's
43 * copy constructor will be invoked by this implementation. Since equal (by value) objects are still separate objects,
44 * the correct behavior is for the constructed object to gain a new ID.
45 *
46 * The assignment operator operator=() similarly has unusual behavior: it does nothing, for similar reasons.
47 *
48 * ### Thread safety, performance ###
49 * All operations safe for simultaneous execution on 2+ separate objects or on the same object.
50 * The ID accessor is a totally trivial accessor. Only construction requires any concurrent-access protection
51 * and internally uses an `atomic` for good efficiency.
52 *
53 * ### Usage patterns ###
54 * This can be used, in particular, to uniquely identify (over all time) objects of any type (across all types).
55 * The most concise usage pattern is probably via public (or possibly private) inheritance. Another is simply via
56 * composition (storage).
57 *
58 * ~~~
59 * void Widget_store::store_in_table(Widget* obj, const Unique_id_holder& obj_id_holder)
60 * {
61 * m_unordered_map[obj_id_holder.unique_id()] = obj;
62 * }
63 *
64 * // Usage pattern 1: Inheritance.
65 * class Widget : ..., public Unique_id_holder // Can change to private inheritance if we always register internally.
66 * {
67 * private:
68 * // Example of an object registering itself.
69 * void some_private_method()
70 * {
71 * ...
72 * s_widget_store.store_in_table(this, *this); // `*this` is itself a Unique_id_holder.
73 * ...
74 * }
75 * static Widget_store s_widget_store;
76 * };
77 * ...
78 * // Example of outside code registering an object.
79 * Widget_store widget_store;
80 * Widget widget;
81 * widget_store::store_in_table(&widget, widget); // `widget` is itself a Unique_id_holder.
82 * ...
83 *
84 * // Usage pattern 2: Composition.
85 * class Widget : ...
86 * {
87 * private:
88 * // Example of an object registering itself.
89 * void some_private_method()
90 * {
91 * ...
92 * s_widget_store.store_in_table(this, m_uniq_id_holder);
93 * ...
94 * }
95 * Unique_id_holder m_uniq_id_holder;
96 * static Widget_store s_widget_store;
97 * };
98 * ~~~
99 *
100 * Update: A simplified pattern is now possible: If you just want the raw unique number itself and don't even
101 * want to keep Unique_id_holder objects around or refer to them, call the static create_unique_id() which returns
102 * a new raw `id_t` integer. The above 2 usage patterns can be rewritten to pass around `id_t` values.
103 * The "Example of outside code registering an object" sub-case is the one exception to that, as for that
104 * either `store_in_table()` must continue to take a Unique_id_holder and not a raw integer, or I suppose
105 * `m_uniq_id_holder` must be made public which some would not like.
106 */
108{
109public:
110 // Types.
111
112 /// Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to reach.
113 using id_t = uint64_t;
114
115 // Constructors/destructor.
116
117 /// Thread-safely construct an ID whose value is different from any other object of this class, past or future.
118 explicit Unique_id_holder();
119
120 /**
121 * This copy constructor is identical in behavior to Unique_id_holder(), the default ctor. Unique_id_holder doc
122 * header explains this behavior and why copy construction is not disallowed entirely.
123 */
124 explicit Unique_id_holder(const Unique_id_holder&);
125
126 // Methods.
127
128 /**
129 * Raw unique ID identifying this object as well as any object of a derived type.
130 *
131 * @return Numeric ID. Can be used as a key for most classic associative containers without extra code.
132 */
133 id_t unique_id() const;
134
135 /**
136 * This assignment operator is a `const` no-op. Unique_id_holder doc
137 * header explains this behavior, and why assignment is not disallowed entirely.
138 *
139 * @return `*this`.
140 */
141 const Unique_id_holder& operator=(const Unique_id_holder&) const;
142
143 /**
144 * Short-hand for `Unique_id_holder().unique_id()`; useful when all you want is the unique integer itself.
145 *
146 * @return See unique_id().
147 */
148 static id_t create_unique_id();
149
150private:
151
152 // Data.
153
154 /// The ID. Note its `const`ness alone forbids a classic assignment operator.
155 const id_t m_id;
156}; // class Unique_id_holder
157
158/**
159 * An integer-like, type-safe wrapper for the *thread-token* concept -- see this_thread_unique_token(), which
160 * explains the whole situation and returns one of these; we are merely the type-safe type usable for it.
161 * A default-constructed Thread_token is "none": it equals no thread's token, ever -- so a data member of this
162 * type needs no explicit not-yet-set sentinel value or initializer.
163 *
164 * @see this_thread_unique_token() that returns a unique (across all threads in this process) Thread_token.
165 *
166 * It converts to #id_t implicitly, so comparisons and the like work as for an integer; while the reverse
167 * requires a deliberate `Thread_token{...}` (type-safety being the whole point). `ostream<<`
168 * prints it in fixed-width hex (see that operator's doc header): a token is an identity, not a quantity.
169 * `Thread_token`s are usable as associate keys, whether ordered or unordered.
170 *
171 * @internal
172 *
173 * ### Impl notes ###
174 * Stylistically there is some inconsistency between Unique_id_holder and Thread_token; for example
175 * Unique_id_holder::create_unique_id() is a `static` member that returns raw values alongside a default-ctor
176 * that creates a Unique_id_holder -- while Thread_token default-ctor returns a sentinel, and free function
177 * this_thread_unique_token() returns a Thread_token. The two are slightly related too. The inconsistency
178 * is neither criminal nor ideal; and the reason for the inconsistency is that Unique_id_holder was written so
179 * long ago; the author (ygoldfel) has developed their style since then. That said Unique_id_holder is fine
180 * too; it probably could be simplified a bit, but it works fine. We are not marking a to-do here, as it would
181 * create unnecessary breaking-code situations, but this all was worth writing down for posterity.
182 */
184{
185 // Types.
186
187 /// Short-hand for the underlying integer type.
189
190 // Data.
191
192 /// The underlying integer value; `0` <=> "none." Typically it is better to assign to `*this` rather than to #m_val.
194
195 // Methods.
196
197 /**
198 * Returns #m_val; so comparisons and the like work as for an integer.
199 * @return See above.
200 */
201 constexpr operator id_t() const;
202}; // struct Thread_token
203static_assert(std::is_trivially_copyable_v<Thread_token> && std::is_trivially_destructible_v<Thread_token>,
204 "Thread_token must remain trivially copyable (at least to store it in raw memory as, say, a prefix) and "
205 "destructible (at least so that any C++ code -- even in a thread_local dtor during TL-deinit "
206 "at thread end -- can safely access a thread_local Thread_token).");
207
208// Free functions: in *_fwd.hpp.
209
210// constexpr implementations.
211
212constexpr Thread_token::operator id_t() const
213{
214 return m_val;
215}
216
217
218} // namespace flow::util
Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from a...
id_t unique_id() const
Raw unique ID identifying this object as well as any object of a derived type.
static id_t create_unique_id()
Short-hand for Unique_id_holder().unique_id(); useful when all you want is the unique integer itself.
Unique_id_holder()
Thread-safely construct an ID whose value is different from any other object of this class,...
const Unique_id_holder & operator=(const Unique_id_holder &) const
This assignment operator is a const no-op.
const id_t m_id
The ID. Note its constness alone forbids a classic assignment operator.
uint64_t id_t
Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to re...
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
An integer-like, type-safe wrapper for the thread-token concept – see this_thread_unique_token(),...
id_t m_val
The underlying integer value; 0 <=> "none." Typically it is better to assign to *this rather than to ...
Unique_id_holder::id_t id_t
Short-hand for the underlying integer type.