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