Flow 2.0.0
Flow project: Full implementation reference.
component_cfg.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 <algorithm>
23#include <utility>
24
25namespace flow::log
26{
27
28// Types.
29
30/**
31 * An internal-use dictionary for fast lookup of small `Cfg` values keyed by
32 * `type_info` objects; impl = 1 `Component_payload_type_dict_by_val_...`
33 * and 1 `Component_payload_type_dict_by_ptr_...`.
34 *
35 * The idea is that Component_payload_type_dict_by_val_via_map and other `Component_payload_type_dict_by_val_...`s
36 * are fast but require that one `insert(X)` only if `&Y = &X` for all `Y == X`; while
37 * the `..._by_val_...` counteparts are slower but work with any `X` types. So an object of the present class
38 * combines both: when one calls `insert(X)`, one specifies whether `type_info X` is guaranteed to live at exactly
39 * that one address; or possibly not. We then store it, and lookup() will try to leverage the stored items so as to
40 * be as fast as it can be (but no faster).
41 *
42 * In order to maximize the chances of a well-performing lookup(), the user shall strive to (correctly!) set
43 * the `type_info_ptr_is_uniq` arg to `true` when calling insert(), whenever possible.
44 *
45 * @tparam Dict_by_ptr_t
46 * A `Component_payload_type_dict_by_ptr_...` template instance.
47 * @tparam Dict_by_var_t
48 * A `Component_payload_type_dict_by_val_...` template instance such that
49 * `Dict_by_ptr_t::Cfg` and `Dict_by_val_t::Cfg` are the same type (meaning they store the same
50 * mapped-payloads).
51 */
52template<typename Dict_by_ptr_t, typename Dict_by_val_t>
54{
55public:
56 // Types.
57
58 /// Convenience alias for template arg.
59 using Dict_by_ptr = Dict_by_ptr_t;
60 /// Convenience alias for template arg.
61 using Dict_by_val = Dict_by_val_t;
62 /// Convenience alias for the mapped-type looked-up by a `*this`.
63 using Cfg = typename Dict_by_ptr::Cfg;
64
65 static_assert(std::is_same_v<Cfg, typename Dict_by_val::Cfg>,
66 "The two dictionary types must hold the same-typed Cfg payloads.");
67
68 // Methods.
69
70 /**
71 * Adds a mapping from the given type to a `Cfg` value, so it can be obtained by lookup().
72 *
73 * @param type
74 * A `typeid(...)` value. Duplicate insert() calls are not allowed: if `this->lookup(X)`, with
75 * `X == type`, has been previously invoked, then behavior is undefined (assertion might trip).
76 * @param cfg
77 * A value.
78 * @param type_info_ptr_is_uniq
79 * Firstly, whenever you can set this to `true`, you should do so in order to improve performance.
80 * Secondly, doing so means you offer the following guarantee: If `this->lookup(X)` is subsequently called,
81 * and `X == type`, then `&X == &type`. If `false` then you're not making this guarantee (note that doesn't
82 * mean you promise there *is* an `X == type` where `&X == &type` -- only that you can't say there isn't).
83 * Tips:
84 * In practice, if `this->insert(type)` and `this->lookup(X)` (with `X == type`) shall always be called
85 * *not* from either side of shared-object boundary, then `type_info_ptr_is_uniq = true` is safe.
86 * Corollary: if all relevant code is statically linked, then it is also safe.
87 */
88 void insert(const std::type_info& type, Cfg cfg, bool type_info_ptr_is_uniq);
89
90 /**
91 * Looks up a value previously added via insert(), or indicates no such value has yet been added.
92 * Informally: we strive to optimize the performance of this method, expecting it to potentially be called
93 * very frequently.
94 *
95 * @param type
96 * A `typeid(...)` value. The lookup shall be successful if and only if `insert(X)` has been called
97 * previously such that `X == type`.
98 * @param cfg
99 * `*cfg` shall be set to the looked-up value previously `insert()`ed; or it shall be left untouched
100 * if not found.
101 * @return `true` if and only if `*cfg` was assigned (i.e., the lookup succeeded).
102 */
103 bool lookup(const std::type_info& type, Cfg* cfg) const;
104
105private:
106 // Data.
107
108 /// The "fast" dictionary (wherein `insert(type, ..., true)`-originated values are stored).
110 /// The "slow" dictionary (wherein `insert(type, ..., false)`-originated values are stored).
112}; // class Component_payload_type_dict
113
114/**
115 * An internal-use dictionary for fast lookup of small `Cfg` values keyed by
116 * `type_info` objects, given the guarantee that any type involved shall have 1 uniquely-addressed such object;
117 * impl = map.
118 *
119 * Note: The API/semantics documented here are shared by all other `Component_payload_type_dict_by_ptr_*` types.
120 *
121 * Formally, the key requirement for using this type (and the equivalent aforementioned ones) is that for any
122 * two `type_info` objects `A` and `B` that might ever be given to `this->insert()` and `this->lookup()`
123 * respectively, if `&A != &B`, then `A != B` as well. I.e., `x = typeid(...)` for one distinct type `...`
124 * shall be guaranteed to yield the same `&x` within a given program invocation, for all `...` used with a `*this`.
125 * In practice, this is the case *if* the relevant `typeid()` calls are
126 * never invoked from translation units with a shared-object boundary between them. (E.g., if everything relevant
127 * is statically linked, then that is the case.)
128 *
129 * ### Rationale ###
130 * This is (at least) potentially used by log::Config, internally, to map Component::payload_type() values to
131 * log-config table indices.
132 *
133 * Note: The API/semantics documented here are also shared by all `Component_payload_type_dict_by_val_*` types
134 * (modulo member `bool S_BY_PTR_ELSE_VAL`) except that the aforementioned `type_info` address
135 * uniqueness guarantee cannot be made for those types. Generally
136 * *our* implementation(s) tend to be faster than their implementation(s), because internally lookup by pointer
137 * is faster than lookup by `std::type_index` which (due to the possibility of dynamic linking) must involve hashing
138 * and/or comparison of type name strings.
139 *
140 * @tparam Map_to_cfg_t
141 * Internally-used map type of choice. It must have the API common to `std::map` and `std::unordered_map`;
142 * and must have `key_type` = `std::type_info*`. In other words, it must be:
143 * `...map<std::type_info*, Cfg_t>`: substitute your `...map` of choice (e.g., a tree or hash map from
144 * Boost or STL); and your small, copyable `Cfg_t` type of choice (e.g., `size_t`).
145 */
146template<typename Map_to_cfg_t>
148{
149public:
150 // Types.
151
152 /// Convenience alias for template arg.
153 using Map_to_cfg = Map_to_cfg_t;
154 /// Convenience alias for the mapped-type looked-up by a `*this`.
155 using Cfg = typename Map_to_cfg::mapped_type;
156
157 // Constants.
158
159 /**
160 * For meta-programming convenience: `true` indicates for us `type_info` equality <=> `&type_info` equality;
161 * meaning we belong to the `Component_payload_type_dict_by_ptr_*` family.
162 */
163 static constexpr bool S_BY_PTR_ELSE_VAL = true;
164
165 // Methods.
166
167 /**
168 * Adds a mapping from the given type to a `Cfg` value, so it can be obtained by lookup().
169 *
170 * @param type
171 * A `typeid(...)` value. Duplicate insert() calls are not allowed: if `this->lookup(X)`, with
172 * `X == type`, has been previously invoked, then behavior is undefined (assertion might trip).
173 * @param cfg
174 * A value.
175 */
176 void insert(const std::type_info& type, Cfg cfg);
177
178 /**
179 * Looks up a value previously added via insert(), or indicates no such value has yet been added.
180 * Informally: we strive to optimize the performance of this method, expecting it to potentially be called
181 * very frequently.
182 *
183 * @param type
184 * A `typeid(...)` value. The lookup shall be successful if and only if `insert(X)` has been called
185 * previously such that `X == type`.
186 * @param cfg
187 * `*cfg` shall be set to the looked-up value previously `insert()`ed; or it shall be left untouched
188 * if not found.
189 * @return `true` if and only if `*cfg` was assigned (i.e., the lookup succeeded).
190 */
191 bool lookup(const std::type_info& type, Cfg* cfg) const;
192
193private:
194 // Data.
195
196 /**
197 * A `...map<type_info*, Cfg>`, wherein insert() simply performs `m_dict[&type] = cfg`.
198 *
199 * ### Performance ###
200 * If it is a tree (sorted) map, it is log-time and composed of quite-quick pointer `<` comparisons.
201 * If it is a hash map, it is amortized constant-time and composed of quite-quick hashing wherein
202 * pointer's value *is* its hash (no calculation required).
203 */
205};
206
207/**
208 * Exactly equivalent to Component_payload_type_dict_by_ptr_via_map but with impl = array with optimized
209 * linear search.
210 *
211 * @tparam Cfg_t
212 * See Component_payload_type_dict_by_ptr_via_map::Cfg.
213 */
214template<typename Cfg_t>
216{
217public:
218 // Types.
219 /// See Component_payload_type_dict_by_ptr_via_map.
220 using Cfg = Cfg_t;
221 // Constants.
222 /// See Component_payload_type_dict_by_ptr_via_map.
223 static constexpr bool S_BY_PTR_ELSE_VAL = true;
224 // Methods.
225 /**
226 * See Component_payload_type_dict_by_ptr_via_map.
227 * @param type
228 * Ditto.
229 * @param cfg
230 * Ditto.
231 */
232 void insert(const std::type_info& type, Cfg cfg);
233 /**
234 * See Component_payload_type_dict_by_ptr_via_map.
235 * @param type
236 * Ditto.
237 * @param cfg
238 * Ditto.
239 * @return Ditto.
240 */
241 bool lookup(const std::type_info& type, Cfg* cfg) const;
242
243private:
244 // Data.
245
246 /**
247 * The values `&type` passed to insert(), in the order in which they were invoked.
248 * (Due to insert() contract: they are guaranteed to be distinct pointer values, no repeats.)
249 *
250 * ### Performance/rationale ###
251 * Conceptually, since we don't sort the keys, a linear search through potentially the entire array
252 * shall be required. However, at a lower level, a number of optimizations may come into play when doing this,
253 * speeding up the constant factor significantly. See lookup() for specific details, but here consider this:
254 * By placing the search keys into a contiguous buffer (as opposed to interleaving the values by using
255 * `vector<pair>` or similar), we have maximized the potential for optimizations (such as a possible `REPNE SCASQ`
256 * instruction). Even if this property ends up not being used, it certainly won't hurt -- and
257 * the code is hardly more complex.
258 */
259 std::vector<const std::type_info*> m_dict_keys;
260
261 /**
262 * The values `cfg` passed to insert(), in the order in which they were invoked.
263 * (Hence `m_dict_vals.size() == m_dict_keys.size()`, with a given value sitting at the same index as its corresponding
264 * key.)
265 */
266 std::vector<Cfg> m_dict_vals;
267};
268
269/**
270 * Exactly equivalent to Component_payload_type_dict_by_ptr_via_array but with impl = sorted array susceptible to
271 * binary search.
272 *
273 * @tparam Cfg_t
274 * See Component_payload_type_dict_by_ptr_via_map::Cfg.
275 */
276template<typename Cfg_t>
278{
279public:
280 // Types.
281 /// See Component_payload_type_dict_by_ptr_via_map.
282 using Cfg = Cfg_t;
283 // Constants.
284 /// See Component_payload_type_dict_by_ptr_via_map.
285 static constexpr bool S_BY_PTR_ELSE_VAL = true;
286 // Methods.
287 /**
288 * See Component_payload_type_dict_by_ptr_via_map.
289 * @param type
290 * Ditto.
291 * @param cfg
292 * Ditto.
293 */
294 void insert(const std::type_info& type, Cfg cfg);
295 /**
296 * See Component_payload_type_dict_by_ptr_via_map.
297 * @param type
298 * Ditto.
299 * @param cfg
300 * Ditto.
301 * @return Ditto.
302 */
303 bool lookup(const std::type_info& type, Cfg* cfg) const;
304
305private:
306 // Data.
307
308 /**
309 * The values pairs (`&type`, `cfg`) passed to insert(), in order of increasing `&type` (pointer) value.
310 * (Due to insert() contract: they are guaranteed to be distinct pointer values, no repeats.)
311 *
312 * ### Performance/rationale ###
313 * The values are sorted by key, so as to be binary-searched for log-time performance. As for the constant
314 * factor: a binary search is a higher-level operation, hence realistically lower-level perfomance is mostly
315 * outside of our control. So we can just use a vanilla setup: a `vector` with key-value `pair`s +
316 * `std::lower_bound()` for binary search.
317 *
318 * The constant factor shall involve the quite-quick pointer `<` comparisons. Versus
319 * Component_payload_type_dict_by_ptr_via_sorted_array (array, but linear search instead of binary search),
320 * we might do better for larger N, while the potentially-low-level-optimized search-through-contiguous-buffer
321 * might cause "them" to do better for smaller N.
322 */
323 std::vector<std::pair<const std::type_info*, Cfg>> m_dict_sorted;
324};
325
326/**
327 * An internal-use dictionary for fast lookup of small `Cfg` values keyed by
328 * `type_info` objects, given NO guarantee that any type involved shall have 1 uniquely-addressed such object;
329 * impl = map.
330 *
331 * Note: The API/semantics documented here are shared by all other `Component_payload_type_dict_by_val_*` types.
332 *
333 * Please see Component_payload_type_dict_by_ptr_via_map. Our API/semantics documented here are the same,
334 * except that one may not use `Component_payload_type_dict_by_ptr_*`, when the `&typeid(...)` uniqueness
335 * (for a given distinct `...`) guarantee cannot be made. So in that case one would use
336 * a `Component_payload_type_dict_by_val_*` (which, informally, will tend to be slower for lookup()).
337 *
338 * @tparam Map_to_cfg_t
339 * Internally-used map type of choice. It must have the API common to `std::map` and `std::unordered_map`;
340 * and must have `key_type` = `std::type_index`. In other words, it must be:
341 * `...map<std::type_index, Cfg_t>`: substitute your `...map` of choice (e.g., a tree or hash map from
342 * Boost or STL); and your small, copyable `Cfg_t` type of choice (e.g., `size_t`).
343 */
344template<typename Map_to_cfg_t>
346{
347public:
348 // Types.
349 /// See Component_payload_type_dict_by_ptr_via_map.
350 using Map_to_cfg = Map_to_cfg_t;
351 /// See Component_payload_type_dict_by_ptr_via_map.
352 using Cfg = typename Map_to_cfg::mapped_type;
353
354 // Constants.
355
356 /**
357 * For meta-programming convenience: `false` indicates for us `type_info` equality <=> `&type_info` equality
358 * is *not* guaranteed; meaning we belong to the `Component_payload_type_dict_by_val_*` family.
359 */
360 static constexpr bool S_BY_PTR_ELSE_VAL = false;
361
362 // Methods.
363 /**
364 * See Component_payload_type_dict_by_ptr_via_map.
365 * @param type
366 * Ditto.
367 * @param cfg
368 * Ditto.
369 */
370 void insert(const std::type_info& type, Cfg cfg);
371 /**
372 * See Component_payload_type_dict_by_ptr_via_map.
373 * @param type
374 * Ditto.
375 * @param cfg
376 * Ditto.
377 * @return Ditto.
378 */
379 bool lookup(const std::type_info& type, Cfg* cfg) const;
380private:
381 // Data.
382
383 /**
384 * A `...map<type_index, Cfg>`, wherein insert() simply performs `m_dict[type_index(type)] = cfg`.
385 *
386 * ### Performance ###
387 * The performance here is not great, actually, which is why ideally one would use
388 * a Component_payload_type_dict_by_val_via_map (or similar) instead of a `*this`; but if `type_info*`
389 * uniqueness cannot be guaranteed, then those classes cannot be used, and one must make do with a `*this`
390 * or similar. Specifically:
391 *
392 * If it is a tree (sorted) map, it is log-time -- but the constant factor is (while technically a black box
393 * inside `std::type_index`) almost certainly involving lexicographic less-than string comparisons, where
394 * the string is more or less `type_info::name()`. In practice that usually contains at least the fully qualified
395 * name of each type being `typeid()`ed. Still... it is potentially not *too* terrible, as the first character
396 * to differ will end a string-compare, and this should typically come pretty early-on.
397 *
398 * If it is a hash map, it is amortized constant-time -- but the constant factor is (while technically a black box
399 * inside `std::type_info::hash_code()`) almost certainly composed of *very slow*
400 * hashing of the aforementioned `type_info::name()`. The constant time sounds nice, but for low N
401 * the cost of constantly hashing `type_info` names can be very high in practice.
402 */
404};
405
406/**
407 * Exactly equivalent to Component_payload_type_dict_by_val_via_map but with impl = array with linear search
408 * (likely by string equality).
409 *
410 * @tparam Cfg_t
411 * See Component_payload_type_dict_by_ptr_via_map::Cfg.
412 */
413template<typename Cfg_t>
415{
416public:
417 // Types.
418 /// See Component_payload_type_dict_by_ptr_via_map.
419 using Cfg = Cfg_t;
420 // Constants.
421 /// See Component_payload_type_dict_by_val_via_map.
422 static constexpr bool S_BY_PTR_ELSE_VAL = false;
423 // Methods.
424 /**
425 * See Component_payload_type_dict_by_ptr_via_map.
426 * @param type
427 * Ditto.
428 * @param cfg
429 * Ditto.
430 */
431 void insert(const std::type_info& type, Cfg cfg);
432 /**
433 * See Component_payload_type_dict_by_ptr_via_map.
434 * @param type
435 * Ditto.
436 * @param cfg
437 * Ditto.
438 * @return Ditto.
439 */
440 bool lookup(const std::type_info& type, Cfg* cfg) const;
441
442private:
443 // Data.
444
445 /**
446 * The values pairs (`&type`, `cfg`) passed to insert(), in the order in which they were invoked.
447 * (Due to insert() contract: they are guaranteed to be distinct values, no repeats whether as pointers
448 * or their dereferenced objects.)
449 *
450 * ### Performance/rationale ###
451 * Conceptually, since we don't sort the keys, a linear search through potentially the entire array
452 * shall be required. The constant factor, however -- unlike in `..._by_ptr_...` impls -- cannot
453 * involve simply searching for a given `type_info*` pointer value: `this->insert(A)` and `this->lookup(B)`
454 * can be called with `A == B` and yet `&A != &B`. So we have no choice but to have the linear-search
455 * perform (almost certainly) string `==` on potentially each value (namely on `type_info::name()` most likely),
456 * and lower-level perfomance is mostly
457 * outside of our control. So we can just use a vanilla setup: a `vector` with key-value `pair`s +
458 * `std::find_if()` for binary search, with the predicate repeatedly checking `*element.first == type` for
459 * each `element` in #m_dict.
460 *
461 * For larger N this is bound to lose to the other `..._by_val_...` alternatives, but for very-low N
462 * (e.g., 1 or 2) it's hard to say.
463 */
464 std::vector<std::pair<const std::type_info*, Cfg>> m_dict;
465};
466
467/**
468 * Exactly equivalent to Component_payload_type_dict_by_val_via_array but with impl = sorted array susceptible to
469 * binary search (likely with string lexicographic less-than comparisons).
470 *
471 * @tparam Cfg_t
472 * See Component_payload_type_dict_by_ptr_via_map::Cfg.
473 */
474template<typename Cfg_t>
476{
477public:
478 // Types.
479 /// See Component_payload_type_dict_by_ptr_via_map.
480 using Cfg = Cfg_t;
481 // Constants.
482 /// See Component_payload_type_dict_by_val_via_map.
483 static constexpr bool S_BY_PTR_ELSE_VAL = false;
484 // Methods.
485 /**
486 * See Component_payload_type_dict_by_ptr_via_map.
487 * @param type
488 * Ditto.
489 * @param cfg
490 * Ditto.
491 */
492 void insert(const std::type_info& type, Cfg cfg);
493 /**
494 * See Component_payload_type_dict_by_ptr_via_map.
495 * @param type
496 * Ditto.
497 * @param cfg
498 * Ditto.
499 * @return Ditto.
500 */
501 bool lookup(const std::type_info& type, Cfg* cfg) const;
502
503private:
504 // Data.
505
506 /**
507 * The values pairs (`&type`, `cfg`) passed to insert(), in order of increasing `type` value,
508 * where `type_info::before()` determines ordering.
509 * (Due to insert() contract: they are guaranteed to be distinct values, no repeats whether as pointers
510 * or their dereferenced objects.)
511 *
512 * ### Performance/rationale ###
513 * The way we work is just like the by-ptr counterpart (Component_payload_type_dict_by_ptr_via_sorted_array),
514 * except that the constant factor of the binary search shall use `type_info::before()` as the ordering
515 * operation repeatedly invoked. Internally this is almost certainly string lexicographic-compare of
516 * `type_info::name()` values which usually contain at least the fully-qualified name of the type
517 * being `typeid()`d.
518 *
519 * For larger N this should improve on our unsorted-array peer, while comparisons against our tree-map (amortized
520 * log-time) and hash-map (amortized constant-time but with extra-awful string hashing involved) are harder
521 * to characterize without empirical testing.
522 */
523 std::vector<std::pair<const std::type_info*, Cfg>> m_dict_sorted;
524};
525
526// Template implementations.
527
528template<typename Map_to_cfg_t>
530{
531#ifndef NDEBUG
532 const auto result =
533#endif
534 m_dict.try_emplace(&type, cfg);
535 assert(result.second && "By contract duplicate insertion is disallowed.");
536}
537
538template<typename Map_to_cfg_t>
540{
541 const auto it = m_dict.find(&type); // Perf: Likely tree-search via pointer<, or hash-search via hash(ptr)=ptr. Good.
542 if (it == m_dict.end())
543 {
544 return false;
545 }
546 // else
547 *cfg = it->second;
548 return true;
549}
550
551template<typename Map_to_cfg_t>
553{
554 const std::type_index key{type};
555
556#ifndef NDEBUG
557 const auto result =
558#endif
559 m_dict.try_emplace(key, cfg);
560 assert(result.second && "By contract duplicate insertion is disallowed.");
561}
562
563template<typename Map_to_cfg_t>
565{
566 const std::type_index key{type};
567 const auto it = m_dict.find(key);
568 // ^-- Perf: Likely tree-search via string<, or hash-search via hash(string), where string = type_info::name(). Yuck.
569 if (it == m_dict.end())
570 {
571 return false;
572 }
573 // else
574 *cfg = it->second;
575 return true;
576}
577
578template<typename Cfg_t>
580{
581 m_dict_keys.emplace_back(&type);
582 m_dict_vals.emplace_back(cfg);
583}
584
585template<typename Cfg_t>
586bool Component_payload_type_dict_by_ptr_via_array<Cfg_t>::lookup(const std::type_info& type, Cfg* cfg) const
587{
588 const auto it_begin = m_dict_keys.begin(); // (Reminder: all these `it`erators are just pointers really.)
589 const auto it_end = m_dict_keys.end();
590
591 const auto it = std::find(it_begin, it_end, &type);
592 /* ^-- Perf notes: First please read doc header on m_dict_keys, then come back here. As noted there:
593 * we've arranged the search values -- pointers (essentially void*s, as far as the specific algorithm involved
594 * cares) -- contiugously in a buffer and are trying to find one simply bitwise-equal to &type. This is
595 * rather prone to optimization. Now, std::find() is of course correct and pithy, but is it fast?
596 * Answer: I (ygoldfel) have looked into reasonably deeply, and the answer is yes, it's pretty good with at
597 * least GCC STL with Linux-64 -- both empirically (~15-30% faster than a simple loop) and in theory. (Re.
598 * the latter -- the "theory": The STL code involves an internally-used find_if()-like helper that is
599 * manually loop-unrolled for the random-accessor-iterator case like ours; and the generated assembly
600 * with -O3 auto-inling performs 4 64-bit CMPs per loop iteration; the processor might parallelize
601 * these as well. So it's pretty good.) Can it be better? Answer: I do not know per se; theoretically
602 * some kind of `REPNE SCASQ` -- etc. etc. -- could possibly squeeze more cycles out of it, though
603 * nothing like that was getting generated automatically even if I tried incrasing m_dict_keys.size() larger
604 * larger. Perhaps such things can be manually inlined here.
605 * @todo Look into all that, if in practice it is deemed worthwhile. */
606
607 if (it == it_end)
608 {
609 return false;
610 }
611 // else
612 *cfg = m_dict_vals[it - it_begin];
613 return true;
614}
615
616template<typename Cfg_t>
618{
619 m_dict.emplace_back(&type, cfg);
620}
621
622template<typename Cfg_t>
623bool Component_payload_type_dict_by_val_via_array<Cfg_t>::lookup(const std::type_info& type, Cfg* cfg) const
624{
625 const auto it_end = m_dict.end();
626
627 const auto it = std::find_if(m_dict.begin(), it_end,
628 [&type](const auto& key_and_val) -> bool { return *key_and_val.first == type; });
629 /* (^-- Impl note: FWIW the std::find() impl of GCC-10 STL is pretty similar to that, using a find_if() equivalent
630 * and a similar functor, just expressed as an explicit class instead of a pithy lambda... but capturing `type`
631 * similarly. I (ygoldfel) have seen std::find() reduce to quite tight assembly code, so we're probably doing
632 * fine too, perf-wise no worse than having a manual loop instead of find_if() -- and prettier code-wise.) */
633
634 if (it == it_end)
635 {
636 return false;
637 }
638 // else
639 *cfg = it->second;
640 return true;
641}
642
643template<typename Cfg_t>
645{
646 /* m_dict_sorted is sorted, so find the insertion-point via binary-search, and insert there to keep it sorted.
647 * This is linear-time, but we care-not about insert() performance: only lookup(). */
648 const auto it = std::lower_bound(m_dict_sorted.begin(), m_dict_sorted.end(), &type,
649 [](const auto& key_and_val, const std::type_info* key) -> bool
650 { return key_and_val.first < key; });
651 m_dict_sorted.emplace(it, &type, cfg);
652}
653
654template<typename Cfg_t>
656{
657 const auto it_end = m_dict_sorted.end();
658
659 /* m_dict_sorted is sorted, so find where `type` pointer is among existing ones, or else where it *would* be
660 * if it isn't among existing ones. */
661 const auto it = std::lower_bound(m_dict_sorted.begin(), it_end, &type,
662 [](const auto& key_and_val, const std::type_info* key) -> bool
663 { return key_and_val.first < key; });
664 // ^-- Perf: You can see the rank-and-file op is a pointer < comparison which is pretty quick.
665
666 if ((it == it_end) || (it->first != &type))
667 {
668 return false;
669 }
670 // else
671 *cfg = it->second;
672 return true;
673}
674
675template<typename Cfg_t>
677{
678 const auto it = std::lower_bound(m_dict_sorted.begin(), m_dict_sorted.end(), &type,
679 [](const auto& key_and_val, const std::type_info* key) -> bool
680 { return key_and_val.first->before(*key); });
681 m_dict_sorted.emplace(it, &type, cfg);
682}
683
684template<typename Cfg_t>
686{
687 const auto it_end = m_dict_sorted.end();
688
689 /* m_dict_sorted is sorted according to type_info::before() order, so find where `type` is in that order among
690 * existing ones, or else where it *would* be if it isn't among existing ones. */
691 const auto it = std::lower_bound(m_dict_sorted.begin(), it_end, &type,
692 [](const auto& key_and_val, const std::type_info* key) -> bool
693 { return key_and_val.first->before(*key); });
694 /* ^-- Perf: The rank-and-file op is type_info::before() which is likely a type_info::name() str-compare, while
695 * type_info::name() = typically compiler-generated static char[] that contains at least the fully-qualified
696 * type name. */
697
698 if ((it == it_end) || (*it->first != type))
699 {
700 return false;
701 }
702 // else
703 *cfg = it->second;
704 return true;
705}
706
707template<typename Dict_by_ptr_t, typename Dict_by_val_t>
709 bool type_info_ptr_is_uniq)
710{
711 if (type_info_ptr_is_uniq)
712 {
713 m_dict_by_type_ptr.insert(type, cfg);
714 }
715 else
716 {
717 m_dict_by_type.insert(type, cfg);
718 }
719}
720
721template<typename Dict_by_ptr_t, typename Dict_by_val_t>
723{
724 return m_dict_by_type_ptr.lookup(type, cfg) || m_dict_by_type.lookup(type, cfg);
725}
726
727} // namespace flow::log
Exactly equivalent to Component_payload_type_dict_by_ptr_via_map but with impl = array with optimized...
bool lookup(const std::type_info &type, Cfg *cfg) const
See Component_payload_type_dict_by_ptr_via_map.
std::vector< Cfg > m_dict_vals
The values cfg passed to insert(), in the order in which they were invoked.
Cfg_t Cfg
See Component_payload_type_dict_by_ptr_via_map.
void insert(const std::type_info &type, Cfg cfg)
See Component_payload_type_dict_by_ptr_via_map.
static constexpr bool S_BY_PTR_ELSE_VAL
See Component_payload_type_dict_by_ptr_via_map.
std::vector< const std::type_info * > m_dict_keys
The values &type passed to insert(), in the order in which they were invoked.
An internal-use dictionary for fast lookup of small Cfg values keyed by type_info objects,...
typename Map_to_cfg::mapped_type Cfg
Convenience alias for the mapped-type looked-up by a *this.
bool lookup(const std::type_info &type, Cfg *cfg) const
Looks up a value previously added via insert(), or indicates no such value has yet been added.
Map_to_cfg_t Map_to_cfg
Convenience alias for template arg.
void insert(const std::type_info &type, Cfg cfg)
Adds a mapping from the given type to a Cfg value, so it can be obtained by lookup().
static constexpr bool S_BY_PTR_ELSE_VAL
For meta-programming convenience: true indicates for us type_info equality <=> &type_info equality; m...
Map_to_cfg m_dict
A ...map<type_info*, Cfg>, wherein insert() simply performs m_dict[&type] = cfg.
Exactly equivalent to Component_payload_type_dict_by_ptr_via_array but with impl = sorted array susce...
bool lookup(const std::type_info &type, Cfg *cfg) const
See Component_payload_type_dict_by_ptr_via_map.
void insert(const std::type_info &type, Cfg cfg)
See Component_payload_type_dict_by_ptr_via_map.
std::vector< std::pair< const std::type_info *, Cfg > > m_dict_sorted
The values pairs (&type, cfg) passed to insert(), in order of increasing &type (pointer) value.
static constexpr bool S_BY_PTR_ELSE_VAL
See Component_payload_type_dict_by_ptr_via_map.
Cfg_t Cfg
See Component_payload_type_dict_by_ptr_via_map.
Exactly equivalent to Component_payload_type_dict_by_val_via_map but with impl = array with linear se...
void insert(const std::type_info &type, Cfg cfg)
See Component_payload_type_dict_by_ptr_via_map.
std::vector< std::pair< const std::type_info *, Cfg > > m_dict
The values pairs (&type, cfg) passed to insert(), in the order in which they were invoked.
Cfg_t Cfg
See Component_payload_type_dict_by_ptr_via_map.
bool lookup(const std::type_info &type, Cfg *cfg) const
See Component_payload_type_dict_by_ptr_via_map.
static constexpr bool S_BY_PTR_ELSE_VAL
See Component_payload_type_dict_by_val_via_map.
An internal-use dictionary for fast lookup of small Cfg values keyed by type_info objects,...
Map_to_cfg m_dict
A ...map<type_index, Cfg>, wherein insert() simply performs m_dict[type_index(type)] = cfg.
typename Map_to_cfg::mapped_type Cfg
See Component_payload_type_dict_by_ptr_via_map.
Map_to_cfg_t Map_to_cfg
See Component_payload_type_dict_by_ptr_via_map.
static constexpr bool S_BY_PTR_ELSE_VAL
For meta-programming convenience: false indicates for us type_info equality <=> &type_info equality i...
void insert(const std::type_info &type, Cfg cfg)
See Component_payload_type_dict_by_ptr_via_map.
bool lookup(const std::type_info &type, Cfg *cfg) const
See Component_payload_type_dict_by_ptr_via_map.
Exactly equivalent to Component_payload_type_dict_by_val_via_array but with impl = sorted array susce...
std::vector< std::pair< const std::type_info *, Cfg > > m_dict_sorted
The values pairs (&type, cfg) passed to insert(), in order of increasing type value,...
bool lookup(const std::type_info &type, Cfg *cfg) const
See Component_payload_type_dict_by_ptr_via_map.
void insert(const std::type_info &type, Cfg cfg)
See Component_payload_type_dict_by_ptr_via_map.
Cfg_t Cfg
See Component_payload_type_dict_by_ptr_via_map.
static constexpr bool S_BY_PTR_ELSE_VAL
See Component_payload_type_dict_by_val_via_map.
An internal-use dictionary for fast lookup of small Cfg values keyed by type_info objects; impl = 1 C...
Dict_by_ptr_t Dict_by_ptr
Convenience alias for template arg.
Dict_by_ptr m_dict_by_type_ptr
The "fast" dictionary (wherein insert(type, ..., true)-originated values are stored).
Dict_by_val m_dict_by_type
The "slow" dictionary (wherein insert(type, ..., false)-originated values are stored).
Dict_by_val_t Dict_by_val
Convenience alias for template arg.
typename Dict_by_ptr::Cfg Cfg
Convenience alias for the mapped-type looked-up by a *this.
void insert(const std::type_info &type, Cfg cfg, bool type_info_ptr_is_uniq)
Adds a mapping from the given type to a Cfg value, so it can be obtained by lookup().
bool lookup(const std::type_info &type, Cfg *cfg) const
Looks up a value previously added via insert(), or indicates no such value has yet been added.
Flow module providing logging functionality.