Flow 2.0.0
Flow project: Full implementation reference.
stat_set_list.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 <type_traits>
23#include <boost/array.hpp>
24
25namespace flow::util::stat
26{
27
28// Types.
29
30/**
31 * Dead-simple collection (essentially a statically-sized array) of up-to-`N` `Stat_set`s, for use
32 * as a lightning-fast way to choose a category and update the `Stat_set` relevant to it.
33 *
34 * So, for example, stats_default() and stats_mutable_default() (a/k/a `stats[_mutable]<0>()`) resolves to,
35 * basically, at compile-time, `(Stat_set*)this + 0`; whereas `stats[_mutable]_at(idx_for_a_particular_category)`
36 * resolves to `(Stat_set*)this + idx_for_a_particular_category` at run-time. Either way, it is just about
37 * as quick as a quasi-associative lookup can be. We've consciously avoided anything dynamic like a hash-map,
38 * or even a `vector` (which, while quick for a read-only lookup, could involve reallocations and associated
39 * difficulties). Dead-simple.
40 *
41 * It really *is* an `array<Stat_set, S_N>` -- initialized to hold #S_N default-constructed `Stat_set`s --
42 * albeit with syntactic sugar for convenient/expressive access.
43 *
44 * @note As a consequence, it is copy-ctible and copy-assignable if and only if the aforementioned `array`
45 * type is. To wit: `Stat_set` is <=> a `Stat_set_list<Stat_set, N>` is. This simple semantic is
46 * why this class template lacks constructors (as does `array` generally).
47 *
48 * Hence:
49 * - `Stat_set_list<Stat_set, N> stats;` // Constructs N things like this: Stat_set{}.
50 * - `auto stats2 = stats;` // Constructs N more like this: Stat_set{stats[idx]}.
51 *
52 * @see Global_stats which can provide global-singleton access to a Stat_set_list.
53 *
54 * @tparam Stat_set_t
55 * #Stat_set is a `Stat_set` of your choice, subject to the patterns and requirements
56 * documented in the namespace util::stat doc header. An additional requirement in this context is
57 * it must be default-constructible: `Stat_set{}`. (It would not be impossible to loosen this
58 * requirement, but it brings certain conveniences, so to the extent the requirement can survive the
59 * user's needs over time, it should.)
60 * @tparam N
61 * The number of `Stat_set`s `*this` can hold. Informally speaking, this does not mean all #S_N
62 * slots *must* be actively used. A suggested pattern is: some module may want to, in the future,
63 * hold 2+ `Stat_set`s depending on the category; but in the meantime it can use `S_N == 1` and
64 * `stats_[mutable_]default()` for everything. Adding more fine-grained stat-keeping later, then, could
65 * involve few to no breaking API changes in this module: perhaps some alias would change from
66 * `using Stats = flow::util::Stat_set_list<My_cool_stats, 1>` to
67 * `using Stats = flow::util::Stat_set_list<My_cool_stats, MAX_COOL_STAT_CATEGORY>`, while
68 * an accessor would remain `Stats& stats()`.
69 */
70template<typename Stat_set_t, size_t N>
72{
73public:
74 // Types.
75
76 /// The template parameter `Stat_set_t`.
77 using Stat_set = Stat_set_t;
78
79 static_assert(std::is_default_constructible_v<Stat_set>,
80 "Stat_set must be default-constructible (T{} required).");
81
82 // Constants.
83
84 /// The template parameter `N`.
85 static constexpr size_t S_N = N;
86
87 static_assert(S_N != 0, "Have to store at least 1 Stat_set.");
88
89 // Methods.
90
91 /**
92 * Read-only access to the 0th `Stat_set`: for stat consumption.
93 *
94 * This slot always exists, as `S_N >= 1`, so this will always compile and execute safely.
95 *
96 * @return See above.
97 */
98 const Stat_set& stats_default() const;
99
100 /**
101 * Mutable access to the 0th `Stat_set`: for stat updates. Otherwise identical to stats_default(); see that.
102 * @return See above.
103 */
105
106 /**
107 * Read-only access to the compile-time-specified-slot-indexed `Stat_set`: for stat consumption.
108 *
109 * Will not compile, if #S_N is too low.
110 *
111 * Rationale: This is not appreciably faster than stats_at() (which takes an `idx` at run-time), but using
112 * it where possible can enhance safety by causing compile errors instead of letting a range-check fail at
113 * run-time.
114 *
115 * @tparam IDX
116 * The slot to access. See also stats_default().
117 * @return See above.
118 */
119 template<size_t IDX>
120 auto stats() const -> const Stat_set&; // @todo See definition for reason for the odd signature form (Doxygen).
121
122 /**
123 * Mutable access to the compile-time-specified-slot-indexed `Stat_set`: for stat updates.
124 * Otherwise identical to stats(); see that.
125 *
126 * @tparam IDX
127 * See above.
128 * @return See above.
129 */
130 template<size_t IDX>
131 auto stats_mutable() -> Stat_set&; // @todo See definition for reason for the odd signature form (Doxygen).
132
133 /**
134 * Read-only access to the run-time-specified-slot-indexed `Stat_set`: for stat consumption.
135 *
136 * If #S_N is too low, behavior is undefined (assertion may trip).
137 *
138 * @param idx
139 * The slot to access.
140 * @return See above.
141 */
142 const Stat_set& stats_at(size_t idx) const;
143
144 /**
145 * Mutable access to the run-time-specified-slot-indexed `Stat_set`: for stat updates.
146 * Otherwise identical to stats_at(); see that.
147 *
148 * @param idx
149 * See above.
150 * @return See above.
151 */
152 Stat_set& stats_mutable_at(size_t idx);
153
154 /// Purely as syntactic sugar: executes `stats_reset(&s, {})` for each `Stat_set s` of #S_N stored in `*this`.
155 void reset();
156
157private:
158 /// Data.
159
160 /// What `*this` is.
161 boost::array<Stat_set, S_N> m_sets;
162}; // class Stat_set_list
163
164/**
165 * A straightforward global/singleton holder of a single `Stat_set_list<Stat_set, S_N>`; the template parameter
166 * #Tag is used to arrange for an independent such `Stat_set_list` per distinct #Tag type.
167 *
168 * The `static` accessor get() is the API:
169 * - get() shall thread-safely construct the `Stat_set_list` the first time it is invoked (even before/after
170 * `main()`).
171 * - get() shall subsequently return ref to that same `Stat_set_list`.
172 * - `Global_stats<Tag, Stat_set, S_N>::get()` shall always return the same address.
173 * - Each distinct, compile-time value of the tuple (#Tag, #Stat_set, #S_N) shall yield
174 * a distinct address returned by get().
175 *
176 * Performance: get() is almost identical in performance to simply returning the value of a `static`/global
177 * pointer variable. In practice it is 1-2 cycles costlier; this is negligible in 99.9999999999% of situations.
178 *
179 * @note One can, instead, simply maintain their own `Stat_set_list<My_cool_stats, NUM_CATS> s_stat_sets`, or
180 * their own on-demand `static` getter. Global_stats provides an alternative way of organizing global
181 * access to such a thing, with segregation by #Tag and on-demand creation; that's all. Your call!
182 *
183 * @tparam Tag_t
184 * #Tag can be any type and is used exclusively (and exclusively at compile-time) to disambiguate
185 * 1+ `Stat_set_list`s with the same #Stat_set and #S_N.
186 * @tparam Stat_set_t
187 * See Stat_set_list.
188 * @tparam N
189 * See Stat_set_list.
190 */
191template<typename Tag_t, typename Stat_set_t, size_t N>
193{
194public:
195 // Types.
196
197 /// The template parameter `Tag_t`.
198 using Tag = Tag_t;
199
200 /// See Stat_set_list.
201 using Stat_set = Stat_set_t;
202
203 // Constants.
204
205 /// See Stat_set_list.
206 static constexpr size_t S_N = N;
207
208 // Methods.
209
210 /**
211 * Thread-safely default-constructs or accesses the one `Stat_set_list<Stat_set, S_N>` for #Tag.
212 * @return See above.
213 */
214 static Stat_set_list<Stat_set, N>& get();
215
216private:
217 // Constructors/destructor.
218
219 /// Forbid instantiation, for it is pointless.
220 Global_stats() = delete;
221}; // class Global_stats
222
223// Template implementations.
224
225template<typename Stat_set_t, size_t N>
226template<size_t IDX>
227auto Stat_set_list<Stat_set_t, N>::stats() const -> const Stat_set_t&
228// Doxygen 1.9.4 gets confused here otherwise; the `->` form is a work-around for that. @todo Revisit with later ver.
229{
230 // Not that pretty, but it is just to avoid copy/pasting the static_assert().
231 return const_cast<Stat_set_list*>(this)->stats_mutable<IDX>();
232}
233
234template<typename Stat_set_t, size_t N>
235template<size_t IDX>
236auto Stat_set_list<Stat_set_t, N>::stats_mutable() -> Stat_set_t& // @todo See above.
237{
238 static_assert(IDX < S_N, "Compile-time Stat_set index exceeds compile-time capacity.");
239 return m_sets[IDX];
240}
241
242template<typename Stat_set_t, size_t N>
243const Stat_set_t& Stat_set_list<Stat_set_t, N>::stats_at(size_t idx) const
244{
245 // Not that pretty, but it is just to avoid copy/pasting the assert().
246 return const_cast<Stat_set_list*>(this)->stats_mutable_at(idx);
247}
248
249template<typename Stat_set_t, size_t N>
251{
252 assert((idx < S_N) && "Run-time Stat_set index exceeds compile-time capacity. "
253 "Can use stats() for compile-time safety, if `idx` is known at compile-time.");
254 return m_sets[idx];
255}
256
257template<typename Stat_set_t, size_t N>
259{
260 return stats<0>();
261}
262
263template<typename Stat_set_t, size_t N>
265{
266 return stats_mutable<0>();
267}
268
269template<typename Stat_set_t, size_t N>
271{
272 for (auto& stats : m_sets)
273 {
274 stats_reset(&stats, {});
275 }
276}
277
278template<typename Tag_t, typename Stat_set_t, size_t N>
280{
281 /* Construct once per <Tag, Stat_set, N> tuple per process.
282 * Compared to just having a static s_singleton data member and returning it here:
283 * - avoids static-init-ordering hell (will be constructed on-demand);
284 * - (after the initial construct) is 1-2 cycles slower (negligible). */
285 static Stat_set_list<Stat_set, S_N> s_singleton;
286 return s_singleton;
287}
288
289} // namespace flow::util::stat
A straightforward global/singleton holder of a single Stat_set_list<Stat_set, S_N>; the template para...
Tag_t Tag
The template parameter Tag_t.
Stat_set_t Stat_set
See Stat_set_list.
Global_stats()=delete
Forbid instantiation, for it is pointless.
static Stat_set_list< Stat_set, N > & get()
Thread-safely default-constructs or accesses the one Stat_set_list<Stat_set, S_N> for Tag.
Dead-simple collection (essentially a statically-sized array) of up-to-N Stat_sets,...
static constexpr size_t S_N
The template parameter N.
void reset()
Purely as syntactic sugar: executes stats_reset(&s, {}) for each Stat_set s of S_N stored in *this.
auto stats() const -> const Stat_set &
Read-only access to the compile-time-specified-slot-indexed Stat_set: for stat consumption.
Stat_set & stats_mutable_default()
Mutable access to the 0th Stat_set: for stat updates.
Stat_set_t Stat_set
The template parameter Stat_set_t.
const Stat_set & stats_default() const
Read-only access to the 0th Stat_set: for stat consumption.
boost::array< Stat_set, S_N > m_sets
Data.
Stat_set & stats_mutable_at(size_t idx)
Mutable access to the run-time-specified-slot-indexed Stat_set: for stat updates.
auto stats_mutable() -> Stat_set &
Mutable access to the compile-time-specified-slot-indexed Stat_set: for stat updates.
const Stat_set & stats_at(size_t idx) const
Read-only access to the run-time-specified-slot-indexed Stat_set: for stat consumption.
Sub-module of flow::util providing lightweight facilities for working with simple structs of stat cou...
Definition: histo.cpp:26
void stats_reset(Stat_set *target_stats, const Stat_set &fresh_stats)
With the help of a freshly-initialized Stat_set, resets the target Stat_set so as to immediately begi...
Definition: stat_set.hpp:467