Flow 1.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
24namespace flow::util
25{
26
27/**
28 * Each object of this class stores (at construction) and returns (on demand) a numeric ID unique from all other
29 * objects of the same class ever constructed, across all time from program start to end. To be clear, the uniqueness
30 * is not just for all existing objects at a given point in time (for which `this` could simply be used instead) but
31 * across all time.
32 *
33 * ### Copying behavior ###
34 * For obvious reasons, Unique_id_holder cannot have standard copy construction and assignment semantics.
35 * It was contemplated to simply make class noncopyable (forbidding copy construction).
36 * However, then no derived type could use auto-generated copy construction and/or `operator=()`, making the
37 * deriving usage pattern (see below) considerably less concise for such types.
38 *
39 * Therefore, it defines a (somewhat unusual) copy constructor: It is simply equal to the default constructor and simply
40 * generates a unique ID. The reason: a common pattern (see below) will involve type `C` deriving from
41 * Unique_id_holder. If `C` happens to use the auto-generated copy constructor implementation, Unique_id_holder's
42 * copy constructor will be invoked by this implementation. Since equal (by value) objects are still separate objects,
43 * the correct behavior is for the constructed object to gain a new ID.
44 *
45 * The assignment operator operator=() similarly has unusual behavior: it does nothing, for similar reasons.
46 *
47 * ### Thread safety, performance ###
48 * All operations safe for simultaneous execution on 2+ separate objects or on the same object.
49 * The ID accessor is a totally trivial accessor. Only construction requires any concurrent-access protection
50 * and internally uses an `atomic` for good efficiency.
51 *
52 * ### Usage patterns ###
53 * This can be used, in particular, to uniquely identify (over all time) objects of any type (across all types).
54 * The most concise usage pattern is probably via public (or possibly private) inheritance. Another is simply via
55 * composition (storage).
56 *
57 * ~~~
58 * void Widget_store::store_in_table(Widget* obj, const Unique_id_holder& obj_id_holder)
59 * {
60 * m_unordered_map[obj_id_holder.unique_id()] = obj;
61 * }
62 *
63 * // Usage pattern 1: Inheritance.
64 * class Widget : ..., public Unique_id_holder // Can change to private inheritance if we always register internally.
65 * {
66 * private:
67 * // Example of an object registering itself.
68 * void some_private_method()
69 * {
70 * ...
71 * s_widget_store.store_in_table(this, *this); // `*this` is itself a Unique_id_holder.
72 * ...
73 * }
74 * static Widget_store s_widget_store;
75 * };
76 * ...
77 * // Example of outside code registering an object.
78 * Widget_store widget_store;
79 * Widget widget;
80 * widget_store::store_in_table(&widget, widget); // `widget` is itself a Unique_id_holder.
81 * ...
82 *
83 * // Usage pattern 2: Composition.
84 * class Widget : ...
85 * {
86 * private:
87 * // Example of an object registering itself.
88 * void some_private_method()
89 * {
90 * ...
91 * s_widget_store.store_in_table(this, m_uniq_id_holder);
92 * ...
93 * }
94 * Unique_id_holder m_uniq_id_holder;
95 * static Widget_store s_widget_store;
96 * };
97 * ~~~
98 *
99 * Update: A simplified pattern is now possible: If you just want the raw unique number itself and don't even
100 * want to keep Unique_id_holder objects around or refer to them, call the static create_unique_id() which returns
101 * a new raw `id_t` integer. The above 2 usage patterns can be rewritten to pass around `id_t` values.
102 * The "Example of outside code registering an object" sub-case is the one exception to that, as for that
103 * either `store_in_table()` must continue to take a Unique_id_holder and not a raw integer, or I suppose
104 * `m_uniq_id_holder` must be made public which some would not like.
105 */
107{
108public:
109 // Types.
110
111 /// Raw integer type to uniquely identify a thing. 64-bit width should make overflow extremely hard to reach.
112 using id_t = uint64_t;
113
114 // Constructors/destructor.
115
116 /// Thread-safely construct an ID whose value is different from any other object of this class, past or future.
117 explicit Unique_id_holder();
118
119 /**
120 * This copy constructor is identical in behavior to Unique_id_holder(), the default ctor. Unique_id_holder doc
121 * header explains this behavior and why copy construction is not disallowed entirely.
122 */
123 explicit Unique_id_holder(const Unique_id_holder&);
124
125 // Methods.
126
127 /**
128 * Raw unique ID identifying this object as well as any object of a derived type.
129 *
130 * @return Numeric ID. Can be used as a key for most classic associative containers without extra code.
131 */
132 id_t unique_id() const;
133
134 /**
135 * This assignment operator is a `const` no-op. Unique_id_holder doc
136 * header explains this behavior, and why assignment is not disallowed entirely.
137 *
138 * @return `*this`.
139 */
140 const Unique_id_holder& operator=(const Unique_id_holder&) const;
141
142 /**
143 * Short-hand for `Unique_id_holder().unique_id()`; useful when all you want is the unique integer itself.
144 *
145 * @return See unique_id().
146 */
147 static id_t create_unique_id();
148
149private:
150
151 // Data.
152
153 /// The ID. Note its `const`ness alone forbids a classic assignment operator.
154 const id_t m_id;
155
156 /**
157 * #m_id value of the Unique_id_holder object last constructed.
158 *
159 * ### Implementation subtleties ###
160 * Using `atomic` for concise thread safety.
161 *
162 * I don't specify any memory ordering arguments when using this
163 * because of the simplicity of this implementation (a lone `++`). Be careful if the code increases in complexity
164 * though; any kind of thread coordination would probably mean one would have to think of such matters and switch
165 * away from just overloaded operator(s) in favor of explicitly-memory-order-specifying `atomic<>` API calls.
166 *
167 * Normally when an `std` implementation is available along with a `boost` one, I choose the latter,
168 * for the likely extra features and for consistency. I chose to do differently in the case of `atomic`, because,
169 * firstly, this is widely recommended, and, secondly, because this is a low-level feature and "feels" like it
170 * might be particularly well coded for a given architecture's compiler. Not that the Boost guys aren't great.
171 */
172 static std::atomic<id_t> s_last_id;
173}; // class Unique_id_holder
174
175} // 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.
static std::atomic< id_t > s_last_id
m_id value of the Unique_id_holder object last constructed.
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...
Definition: basic_blob.hpp:29