Flow 2.0.0
Flow project: Full implementation reference.
util.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
23#include <boost/lexical_cast.hpp>
24#include <boost/algorithm/string.hpp>
25
26namespace flow::util
27{
28
29// Types.
30
31/**
32 * An empty interface, consisting of nothing but a default `virtual` destructor, intended as a boiler-plate-reducing
33 * base for any other (presumably `virtual`-method-having) class that would otherwise require a default `virtual`
34 * destructor.
35 *
36 * Usually, if you have a base class `C` at the top of a `virtual`-method-having hierarchy, then it needs a `virtual`
37 * destructor, even if it is `= default` or `{}`. Otherwise, trying to delete an object of subclass `C2 : public C`
38 * via a `C*` pointer will fail to call destructor `~C2()` -- which may not be empty, causing leaks and so on.
39 * Declaring `~C()` and its empty implementation is surprisingly verbose. So, instead, don't; and `public`ly derive
40 * from Null_interface instead.
41 *
42 * It is particularly useful for interface classes.
43 */
45{
46public:
47 // Destructor.
48
49 /**
50 * Boring `virtual` destructor.
51 *
52 * Why is it pure? Main reason: Then Null_interface becomes abstract and cannot be itself instantiated, which is good.
53 * Otherwise we'd need a `protected` constructor or something to prevent it.
54 *
55 * I (ygoldfel) actually thought this means a subclass *has* to now define a body (even if merely `= default` or
56 * `{}`): but no: the compiler will (and must) generate an empty (and, because of us, `virtual`) destructor for any
57 * subclass that doesn't explicitly define one. A destructor isn't a regular method, so that's how it works.
58 * There will not be a linker error.
59 */
60 virtual ~Null_interface() = 0;
61};
62
63/**
64 * Useful as a no-unique-address private member to make a type noncopyable while keeping that type an aggregate
65 * (can be direct-initialized).
66 *
67 * So you can do: `[[no_unique_address]] flow::util::Noncopyable m_nc{};`.
68 *
69 * ### Rationale ###
70 * The usual technique of deriving from `boost::noncopyable` disables aggregateness. In C++20 declaring
71 * a `= delete` copy ctor also disables it. This trick still works though.
72 */
74{
75 // Constructors/destructor.
76
77 /// Makes it possible to instantiate.
78 Noncopyable() = default;
79 /// Forbid copying.
80 Noncopyable(const Noncopyable&) = delete;
81
82 // Methods.
83
84 /// Forbid copying.
85 void operator=(const Noncopyable&) = delete;
86};
87
88/**
89 * A simple RAII-pattern class template that, at construction, sets the specified location in memory to a specified
90 * value, memorizing the previous contents; and at destruction restores the value. E.g.:
91 *
92 * ~~~
93 * thread_local int s_this_thread_val;
94 * ...
95 * {
96 * Scoped_setter<int> setter(&s_this_thread_val, 75); // Set it to 75 and memorize (whatever).
97 * ...
98 * {
99 * Scoped_setter<int> setter(&s_this_thread_val, 125); // Set it to 125 and memorize 75.
100 * ...
101 * } // Restored from 125 to 75.
102 * ...
103 * } // Restored from (probably) 75 to (whatever).
104 * ~~~
105 *
106 * The object is movable, not copyable (which is similar to `unique_ptr`) to prevent "double-restore." Related:
107 * one can easily return customized auto-setter/restorers:
108 *
109 * ~~~
110 * thread_local Widget s_widget;
111 * auto widget_setter_auto(Widget&& widget_moved)
112 * {
113 * return flow::util::Scoped_setter<Widget>(&s_widget, std::move(widget_moved));
114 * }
115 * ...
116 * { // Later, some block: Set s_widget. Code here doesn't even know/care a Scoped_setter is involved.
117 * const auto setter_auto = widget_setter_auto({ ...widget-init... });
118 * ...
119 * } // Restore s_widget.
120 * ~~~
121 *
122 * ### Thready safety ###
123 * This is a simple object: it just performs a few assignments without any added
124 * concurrency protection. If the memory location can be accessed simultaneously by other threads, watch out.
125 *
126 * In particular it's a good fit for thread-local locations: `&X`, where `X` is declared `thread_local`, or
127 * `X == *(P.get())` where `P` is a `boost::thread_specific_ptr`.
128 *
129 * @tparam Value
130 * The stored type, which must be move-assignable and move-constructible.
131 * All `Value` writes are performed using exclusively these operations.
132 * Informally: For best performance when `Value` is a heavy-weight type, these operations should be
133 * be written to be light-weight, such as in terms of swapping a few scalars.
134 * In particular this is already the case for all STL-compliant container types.
135 *
136 * @internal
137 * ### Implementation ###
138 * An alternative implementation, which could even be reduced to just an alias, would have used `unique_ptr`.
139 * However in this case I (ygoldfel) wanted maximum control for perf. The use case originally driving this was
140 * the thread-local verbosity override: log::Config::this_thread_verbosity_override_auto(). flow::log is fairly
141 * paranoid about performance, in general, although admittedly this particular call isn't necessarily ubiquitous.
142 */
143template<typename Value>
145{
146public:
147 // Constructors/destructor.
148
149 /**
150 * Post-condition: `*target` contains was `val_src_moved` contained at ctor entry; and the destructor invocation shall
151 * reverse this, so that `*target` is restored to its value at entry.
152 *
153 * `*this` cannot be copied, but it can be moved. As a result, it is guaranteed that the aforementioned destructor
154 * will execute exactly once; however it can be move-constructed-from-`*this` other Scope_setter's destructor,
155 * while our own dtor therefore is a no-op.
156 *
157 * @param target
158 * The target object that shall be set to `val_src_moved` now and restored in our, or moved-object's, dtor.
159 * The current value of `*target` is saved internally via assignment of `move(*target)`.
160 * Behavior undefined (assertion may trip) if null.
161 * @param val_src_moved
162 * Value to save to `*target` immediately, via assignment of `move(val_src_moved)`.
163 */
164 explicit Scoped_setter(Value* target, Value&& val_src_moved);
165
166 /**
167 * Move constructor: `*this` acts as `src_moved` would-have, while `src_moved` becomes a no-op object permanently.
168 *
169 * @param src_moved
170 * Source object. Its destructor shall do nothing after this returns.
171 */
172 Scoped_setter(Scoped_setter&& src_moved);
173
174 /// Prohibit copying: for each `explicit` ctor invocation, there shall be exactly 1 non-no-op dtor invocation.
175 Scoped_setter(const Scoped_setter&) = delete;
176
177 /**
178 * Restores `*target` (from main ctor) to its value at entry to said ctor; or does nothing if `*this` has been
179 * moved-from via the move ctor.
180 */
182
183 // Methods.
184
185 /// Prohibit copying (see `delete`d copy ctor).
187
188 /// Prohibit modifying existing `*this`; except that moving-from is enabled via the move ctor.
190
191private:
192 // Data.
193
194 /// Target object location; see ctors; if null then this is a moved-from Scoped_setter that intentionally no-ops.
196
197 /// If and only if #m_target_or_null is non-null, this saves `*m_target_or_null`. Otherwise meaningless.
199}; // class Scoped_setter
200
201// Template implementations.
202
203// Scoped_setter template implementations.
204
205template<typename Value>
206Scoped_setter<Value>::Scoped_setter(Value* target, Value&& val_src_moved) :
207 m_target_or_null(target),
208 m_saved_value(std::move(*m_target_or_null))
209{
210 *m_target_or_null = std::move(val_src_moved);
211}
212
213template<typename Value>
214Scoped_setter<Value>::Scoped_setter(Scoped_setter&& src_moved) : // =default might work fine but to be clear/certain:
215 m_target_or_null(src_moved.m_target_or_null),
216 m_saved_value(std::move(src_moved.m_saved_value))
217{
218 assert(m_target_or_null && "Should not be moving-from a thing that has already been moved-from.");
219
220 src_moved.m_target_or_null = nullptr;
221 // As promised: Now src_moved's dtor will no-op.
222}
223
224template<typename Value>
226{
227 if (m_target_or_null)
228 {
229 *m_target_or_null = std::move(m_saved_value);
230 }
231 // else { `*this` must have been moved-from. No-op. }
232}
233
234// Free function template implementations.
235
236template<typename Time_unit, typename N_items>
237double to_mbit_per_sec(N_items items_per_time, size_t bits_per_item)
238{
239 /* Let there be U/W seconds per Time_unit. Then the following holds:
240 *
241 * items_per_time items/Time_units * W/U Time_units/second * bits_per_item bits/item
242 * * 1/(1000*1000) megabits/bits
243 * = items_per_time * W / U * bits_per_item / 1000 / 1000 megabits/second.
244 *
245 * Notice the units work out. W and U are conveniently available in Time_unit::period, which is a boost::ratio. */
246
247 return
248 /* This zealously converts everything to double ASAP to avoid overflow. Could probably speed things up a bit
249 * by postponing some of those conversions until after some integer multiplications, but then overflows could
250 * creep in. It's best not to assume too much about the values of den and num, as this function is meant to
251 * be rather adaptable to various situations. I did try to avoid unnecessary divisions though in favor of
252 * multiplications, sort of guessing the latter are faster. Or not... *shrug*. */
253 double(items_per_time) * double(bits_per_item) * double(Time_unit::period::den)
254 / (double(Time_unit::period::num) * double(1000 * 1000));
255}
256
257template<typename Integer>
258Integer ceil_div(Integer dividend, Integer divisor)
259{
260 // ceil(A : B) = (A + B - 1) / B, where : is floating point division, while / is integer division.
261 static_assert(std::is_integral_v<Integer>, "ceil_div<T>: T must be an integer type.");
262 assert(dividend >= 0);
263 assert(divisor > 0);
264
265 return (dividend + divisor - 1) / divisor;
266 /* (Could one do further bitwise trickery? Perhaps but let optimizer do it. Wouldn't optimizer also just
267 * optimize a literal floating-point `ceil(a / b)`? Well, no. Probably not. So we wrote this function.) */
268}
269
270template<typename T>
271bool in_closed_range(T const & min_val, T const & val, T const & max_val)
272{
273 // This writes "(min_val <= val) && (val <= max_val)" by using only <, to support the greatest number of types.
274 return ((min_val < val) || (!(val < min_val))) &&
275 ((val < max_val) || (!(max_val < val)));
276}
277
278template<typename T>
279bool in_open_closed_range(T const & min_val, T const & val, T const & max_val)
280{
281 // This writes "(min_val < val) && (val <= max_val)" by using only <, to support the greatest number of types.
282 return (min_val < val) &&
283 ((val < max_val) || (!(max_val < val)));
284}
285
286template<typename T>
287bool in_closed_open_range(T const & min_val, T const & val, T const & max_val)
288{
289 // This writes "(val < max_val) && (min_val <= val)" by using only <, to support the greatest number of types.
290 return (val < max_val) &&
291 ((min_val < val) || (!(val < min_val)));
292}
293
294template<typename T>
295bool in_open_open_range(T const & min_val, T const & val, T const & max_val)
296{
297 return (min_val < val) && (val < max_val);
298}
299
300template<typename Container>
301bool key_exists(const Container& container, const typename Container::key_type& key)
302{
303 return container.find(key) != container.end();
304}
305
306template<typename Cleanup_func>
307Auto_cleanup setup_auto_cleanup(const Cleanup_func& func)
308{
309 /* This trick, from shared_ptr or bind Boost docs (if I recall correctly), uses shared_ptr's deleter feature. The
310 * Auto_cleanup gains "ownership" of null pointer, purely for the purpose of running a deleter on it when the object
311 * goes out of scope sometime later. Deleting 0 itself would have been useless no-op, and instead we ignore the null
312 * and simply call user's func(), which is what the goal is.
313 *
314 * Subtlety: shared_ptr docs say the passed-in deleter is saved by copy, so we needn't worry about it disappearing
315 * after we return.
316 *
317 * Subtlety: we need to make a copy (via capture) of func, as there's zero guarantee (and low likelihood in practice)
318 * that func is a valid object at the time cleanup is actually needed (sometime after we return). */
319 return Auto_cleanup(static_cast<void*>(0),
320 [func](void*) { func(); });
321}
322
323template<typename Minuend, typename Subtrahend>
324bool subtract_with_floor(Minuend* minuend, const Subtrahend& subtrahend, const Minuend& floor)
325{
326 assert(minuend);
327
328 /* Basically just avoid implicit conversions and anything that mind overflow or underflow.
329 * The one underflow we allow is the subtraction of `floor`: doc header says keep `floor` small.
330 * So it's their problem if it's not. */
331
332 const Minuend converted_subtrahend = Minuend(subtrahend);
333
334 // min - sub <= floor <===> min - floor <= sub.
335 if ((*minuend - floor) <= converted_subtrahend)
336 {
337 *minuend = floor;
338 return false;
339 }
340 // else
341 *minuend -= converted_subtrahend;
342 return true;
343}
344
345template<typename From, typename To>
346size_t size_unit_convert(From num_froms)
347{
348 return ((num_froms * sizeof(From)) + sizeof(To) - 1) / sizeof(To);
349}
350
351template<typename T1, typename ...T_rest>
352void feed_args_to_ostream(std::ostream* os, T1 const & ostream_arg1, T_rest const &... remaining_ostream_args)
353{
354 // Induction step for variadic template.
355 feed_args_to_ostream(os, ostream_arg1);
356 feed_args_to_ostream(os, remaining_ostream_args...);
357}
358
359template<typename T>
360void feed_args_to_ostream(std::ostream* os, T const & only_ostream_arg)
361{
362 // Induction base.
363 *os << only_ostream_arg;
364}
365
366template<typename ...T>
367void ostream_op_to_string(std::string* target_str, T const &... ostream_args)
368{
369 using std::flush;
370
371 /* Pushes characters directly onto an `std::string`, instead of doing so into an `ostringstream` and then getting it
372 * by copy via `ostringstream::copy()`. This is for performance and may make a large difference
373 * overall, if this is used in logging for example. However, Thread_local_string_appender accomplishes
374 * better performance still and some other features. */
375 String_ostream os(target_str);
376 feed_args_to_ostream(&(os.os()), ostream_args...);
377 os.os() << flush;
378}
379
380template<typename ...T>
381std::string ostream_op_string(T const &... ostream_args)
382{
383 using std::string;
384
385 string result;
386 ostream_op_to_string(&result, ostream_args...);
387 return result;
388}
389
390template<typename Map, typename Sequence>
392 (Sequence const & src_seq, Map* target_map,
393 const Function<typename Map::mapped_type (size_t)>& idx_to_map_val_func)
394{
395 size_t idx = 0;
396 for (const auto& src_element : src_seq)
397 {
398 (*target_map)[src_element] = idx_to_map_val_func(idx);
399 ++idx;
400 }
401}
402
403template<typename Map, typename Sequence>
404void sequence_to_inverted_lookup_map(Sequence const & src_seq, Map* target_map)
405{
406 sequence_to_inverted_lookup_map(src_seq, target_map, [](size_t idx) -> size_t
407 {
408 return idx;
409 });
410}
411
412template<typename Const_buffer_sequence>
413std::ostream& buffers_to_ostream(std::ostream& os,
414 const Const_buffer_sequence& data,
415 const std::string& indentation,
416 size_t bytes_per_line)
417{
418 using boost::io::ios_fill_saver;
419 using boost::io::ios_flags_saver;
420 using boost::io::ios_width_saver;
421 using boost::asio::buffers_iterator;
422 using std::isprint;
423
424 /* This sweet type will iterate over the buffer sequence (jumping between contained buffers, if ther are > 1).
425 * If `Bufs_iter it`, then *it is of type uint8_t. */
426 using Bufs_iter = buffers_iterator<Const_buffer_sequence, uint8_t>;
427
428 constexpr size_t BYTES_PER_LINE_DEFAULT = 16;
429 bool single_line_mode = false;
430 if (bytes_per_line == 0)
431 {
432 bytes_per_line = BYTES_PER_LINE_DEFAULT;
433 }
434 else if (bytes_per_line == size_t(-1))
435 {
436 /* Firstly just pretend exactly the bytes in the whole input = "max" bytes per line.
437 * This accomplishes the bulk of what "single-line mode" means. */
438 bytes_per_line = buffer_size(data);
439 // A bit slow potentially to need to enumerate all scattered buffers. Eh. Contract said we should be assumed slow.
440
441 // The rest of what it means is lacking a newline at the end in single-line mode. So just remember that part.
442 single_line_mode = true;
443 }
444
445 // Ensure format settings return to their previous values subsequently.
446 ios_flags_saver flags_saver(os);
447 ios_fill_saver fill_saver(os);
448 ios_width_saver width_saver(os);
449
450 /* Set formatting and output numeric value (hex) of first byte.
451 * @todo Is there a way to write this with manipulators too? */
452 os.setf(std::ios::right | std::ios::hex, std::ios::adjustfield | std::ios::basefield);
453 os << std::setfill('0');
454
455 const Bufs_iter end_byte_it = Bufs_iter::end(data);
456
457 for (Bufs_iter cur_byte_it = Bufs_iter::begin(data);
458 cur_byte_it != end_byte_it;
459 /* Advancing of cur_byte_it occurs within body of loop. */)
460 {
461 // The for() loop around us guarantees there is at least this first byte. Print the numeric value.
462 os << indentation << '['
463 << std::setw(2) << int(*cur_byte_it); // Numeric value in hex.
464
465 // Repeat for remaining bytes left in this line. Stop at bytes/line limit, or if reached end of buffers.
466 size_t n_bytes_printed;
467 for ((n_bytes_printed = 1), ++cur_byte_it; // Account for printing that first byte above.
468 (n_bytes_printed != bytes_per_line) && (cur_byte_it != end_byte_it);
469 ++cur_byte_it, ++n_bytes_printed)
470 {
471 os << ' ' << std::setw(2) << int(*cur_byte_it); // Numeric value in hex.
472 }
473
474 // Spaces as if rest of line still had a few ghost values to print (space + 2 spaces for the hex digits).
475 for (size_t n_bytes_printed_including_padding = n_bytes_printed;
476 n_bytes_printed_including_padding != bytes_per_line;
477 ++n_bytes_printed_including_padding)
478 {
479 os << " ";
480 }
481
482 // Backtrack and print those same bytes -- this time as printable characters (when printable, else dots).
483 cur_byte_it -= n_bytes_printed;
484
485 os << '|';
486 for (size_t n_chars_printed = 0;
487 n_chars_printed != n_bytes_printed;
488 ++cur_byte_it, ++n_chars_printed)
489 {
490 char c = *cur_byte_it;
491 os << (isprint(c) ? c : '.');
492 }
493 os << ']';
494 if (!single_line_mode)
495 {
496 os << '\n';
497 }
498 } // for (cur_byte_it)
499
500 return os;
501
502 // The formatting changes will be restored here as the savers exit scope.
503} // buffers_to_ostream()
504
505template<typename Const_buffer_sequence>
506std::string buffers_dump_string(const Const_buffer_sequence& data, const std::string& indentation,
507 size_t bytes_per_line)
508{
509 using std::flush;
510 using std::string;
511
512 // See comment in ostream_op_to_string() which applies here too (re. perf).
513
514 string target_str;
515 String_ostream os(&target_str);
516 buffers_to_ostream(os.os(), data, indentation, bytes_per_line);
517 os.os() << flush;
518
519 return target_str;
520}
521
522template<typename Enum>
523Enum istream_to_enum(std::istream* is_ptr, Enum enum_default, Enum enum_sentinel,
524 bool accept_num_encoding, bool case_sensitive,
525 Enum enum_lowest)
526{
527 using boost::lexical_cast;
528 using boost::bad_lexical_cast;
529 using boost::algorithm::equals;
530 using boost::algorithm::is_iequal;
531 using std::locale;
532 using std::string;
533 using std::isdigit;
534 using std::isalnum;
535 using Traits = std::char_traits<char>;
536 using enum_t = std::underlying_type_t<Enum>;
537
538 // Reminder: There are various assumptions about Enum this takes for granted; behavior undefined otherwise.
539
540 assert(enum_t(enum_lowest) >= 0); // Otherwise we'd have to allow '-' (minus sign), and we'd... just rather not.
541 auto& is = *is_ptr;
542 const is_iequal i_equal_func(locale::classic());
543
544 // Read into `token` until (and not including) the first non-alphanumeric/underscore character or stream end.
545 string token;
546 char ch;
547 while (((ch = is.peek()) != Traits::eof()) && (isalnum(ch) || (ch == '_')))
548 {
549 token += ch;
550 is.get();
551 }
552
553 Enum val = enum_default;
554
555 if (!token.empty())
556 {
557 if (accept_num_encoding && isdigit(token.front())) // Hence ostream<< shouldn't serialize a digit-leading value.
558 {
559 enum_t num_enum;
560 try
561 {
562 num_enum = lexical_cast<enum_t>(token);
563 // This assumes a vanilla enum integer value ordering.
564 if ((num_enum >= enum_t(enum_sentinel) || (num_enum < enum_t(enum_lowest))))
565 {
566 num_enum = enum_t(enum_default);
567 }
568 val = Enum(num_enum);
569 }
570 catch (const bad_lexical_cast& exc)
571 {
572 assert(val == enum_default);
573 }
574 } // if (accept_num_encoding && isdigit())
575 else // if (!(accept_num_encoding && isdigit()))
576 {
577 enum_t idx;
578 // This assumes a vanilla enum integer value ordering within this [closed range].
579 for (idx = enum_t(enum_lowest); idx != enum_t(enum_sentinel); ++idx)
580 {
581 const auto candidate = Enum(idx);
582 /* Note -- lexical_cast<string>(Enum) == (operator<<(ostringstream&, Enum)).str() -- the symbolic
583 * encoding of Enum (as we promised to accept, case-[in]sensitively), not the numeric encoding. The numeric
584 * encoding is checked-for in the `if (accept_num_encoding...)` branch above using a non-looping technique. */
585 if (case_sensitive ? equals(token, lexical_cast<string>(candidate))
586 : equals(token, lexical_cast<string>(candidate), i_equal_func))
587 {
588 val = candidate;
589 break;
590 }
591 }
592 assert((idx != enum_t(enum_sentinel)) || (val == enum_default));
593 } // else if (!(accept_num_encoding && isdigit()))
594 } // if (!token.empty())
595
596 return val;
597} // istream_to_enum()
598
599} // namespace flow::util
An empty interface, consisting of nothing but a default virtual destructor, intended as a boiler-plat...
Definition: util.hpp:45
virtual ~Null_interface()=0
Boring virtual destructor.
A simple RAII-pattern class template that, at construction, sets the specified location in memory to ...
Definition: util.hpp:145
~Scoped_setter()
Restores *target (from main ctor) to its value at entry to said ctor; or does nothing if *this has be...
Definition: util.hpp:225
Value m_saved_value
If and only if m_target_or_null is non-null, this saves *m_target_or_null. Otherwise meaningless.
Definition: util.hpp:198
Scoped_setter(const Scoped_setter &)=delete
Prohibit copying: for each explicit ctor invocation, there shall be exactly 1 non-no-op dtor invocati...
Scoped_setter & operator=(const Scoped_setter &)=delete
Prohibit copying (see deleted copy ctor).
Value * m_target_or_null
Target object location; see ctors; if null then this is a moved-from Scoped_setter that intentionally...
Definition: util.hpp:195
Scoped_setter(Value *target, Value &&val_src_moved)
Post-condition: *target contains was val_src_moved contained at ctor entry; and the destructor invoca...
Definition: util.hpp:206
Scoped_setter & operator=(Scoped_setter &&)=delete
Prohibit modifying existing *this; except that moving-from is enabled via the move ctor.
Similar to ostringstream but allows fast read-only access directly into the std::string being written...
std::ostream & os()
Access to stream that will write to owned string.
Flow module containing miscellaneous general-use facilities that don't fit into any other Flow module...
Definition: basic_blob.hpp:29
bool in_closed_open_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a [low,...
Definition: util.hpp:287
bool key_exists(const Container &container, const typename Container::key_type &key)
Returns true if and only if the given key is present at least once in the given associative container...
Definition: util.hpp:301
Auto_cleanup setup_auto_cleanup(const Cleanup_func &func)
Provides a way to execute arbitrary (cleanup) code at the exit of the current block.
Definition: util.hpp:307
void sequence_to_inverted_lookup_map(Sequence const &src_seq, Map *target_map, const Function< typename Map::mapped_type(size_t)> &idx_to_map_val_func)
Similar to the 2-arg overload of sequence_to_inverted_lookup_map() but with the ability to store a va...
Definition: util.hpp:392
double to_mbit_per_sec(N_items items_per_time, size_t bits_per_item)
Utility that converts a bandwidth in arbitrary units in both numerator and denominator to the same ba...
Definition: util.hpp:237
std::string buffers_dump_string(const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line)
Identical to buffers_to_ostream() but returns an std::string instead of writing to a given ostream.
Definition: util.hpp:506
Enum istream_to_enum(std::istream *is_ptr, Enum enum_default, Enum enum_sentinel, bool accept_num_encoding, bool case_sensitive, Enum enum_lowest)
Deserializes an enum class value from a standard input stream.
Definition: util.hpp:523
bool subtract_with_floor(Minuend *minuend, const Subtrahend &subtrahend, const Minuend &floor)
Performs *minuend -= subtrahend, subject to a floor of floor.
Definition: util.hpp:324
size_t size_unit_convert(From num_froms)
Answers the question what's the smallest integer number of Tos sufficient to verbatim store the given...
Definition: util.hpp:346
std::string ostream_op_string(T const &... ostream_args)
Equivalent to ostream_op_to_string() but returns a new string by value instead of writing to the call...
Definition: util.hpp:381
Integer ceil_div(Integer dividend, Integer divisor)
Returns the result of the given non-negative integer divided by a positive integer,...
Definition: util.hpp:258
bool in_open_open_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
Definition: util.hpp:295
bool in_open_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, given as a (low,...
Definition: util.hpp:279
void ostream_op_to_string(std::string *target_str, T const &... ostream_args)
Writes to the specified string, as if the given arguments were each passed, via << in sequence,...
Definition: util.hpp:367
std::ostream & buffers_to_ostream(std::ostream &os, const Const_buffer_sequence &data, const std::string &indentation, size_t bytes_per_line)
Writes a multi- or single-line string representation of the provided binary data to an output stream,...
Definition: util.hpp:413
bool in_closed_range(T const &min_val, T const &val, T const &max_val)
Returns true if and only if the given value is within the given range, inclusive.
Definition: util.hpp:271
boost::shared_ptr< void > Auto_cleanup
Helper type for setup_auto_cleanup().
Definition: util_fwd.hpp:205
void feed_args_to_ostream(std::ostream *os, T1 const &ostream_arg1, T_rest const &... remaining_ostream_args)
"Induction step" version of variadic function template that simply outputs arguments 2+ via << to the...
Definition: util.hpp:352
Useful as a no-unique-address private member to make a type noncopyable while keeping that type an ag...
Definition: util.hpp:74
Noncopyable(const Noncopyable &)=delete
Forbid copying.
void operator=(const Noncopyable &)=delete
Forbid copying.
Noncopyable()=default
Makes it possible to instantiate.