Flow 1.0.0
Flow project: Full implementation reference.
Classes | Namespaces | Macros | Functions
option_set.hpp File Reference
#include "flow/cfg/cfg_fwd.hpp"
#include "flow/log/log.hpp"
#include "flow/error/error.hpp"
#include "flow/cfg/detail/cfg_fwd.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/unordered_set.hpp>

Go to the source code of this file.

Classes

class  flow::cfg::Option_set_base
 Un-templated base for Option_set. More...
 
class  flow::cfg::Option_set< Value_set >
 The core config-parsing facility, which builds parsing/comparison/output capabilities on top of a given simple config-holding object, of the type Value_set, a template argument to this class template. More...
 
struct  flow::cfg::Option_set< Value_set >::Declare_options_func_args
 Internal-use structure to use with Declare_options_func callback. More...
 

Namespaces

namespace  flow
 Catch-all namespace for the Flow project: A collection of various production-quality modules written in modern C++17, originally by ygoldfel.
 
namespace  flow::cfg
 Flow module that facilitates configuring modules, such as applications and APIs, via statically and/or dynamically parsed sets of name/value pairs from config sources like files and command lines.
 

Macros

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION(ARG_m_value, ARG_description, ARG_bool_validate_expr)    FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(ARG_m_value, ARG_description, ARG_bool_validate_expr, false)
 Macro the user must use, as prescribed in the flow::cfg::Option_set constructor doc header discussing declare_opts_func in their flow::cfg::Option_set::Declare_options_func callback, when declaring each individual config option as linked to a given data member of their config-bearing struct used as the Value_set in Option_set<Value_set>. More...
 
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC(ARG_m_value, ARG_description, ARG_bool_validate_expr)    FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(ARG_m_value, ARG_description, ARG_bool_validate_expr, true)
 Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION(), except the option is marked as non-accumulating, which means that each time a config source (such as file) is parsed, this option's value is reset to default and then only overwritten with a potential non-default value if explicitly specified in the config source. More...
 
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED(ARG_m_value, ARG_description, ARG_bool_validate_expr, ARG_key)
 Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION(), but with support for setting a value at a container subscript. More...
 
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED_NO_ACC(ARG_m_value, ARG_description, ARG_bool_validate_expr, ARG_key)
 Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED_NO_ACC(), except the option is marked as non-accumulating in the same sense as for FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC(). More...
 
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(ARG_m_value, ARG_description, ARG_bool_validate_expr, ARG_no_accumulation)
 As of this writing is identical to either FLOW_CFG_OPTION_SET_DECLARE_OPTION() or FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC(), depending on the value of ARG_no_accumulation argument. More...
 
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED(ARG_m_value, ARG_opt_name_c_str, ARG_description, ARG_bool_validate_expr, ARG_no_accumulation)
 Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(), except the user must specify the option's string name manually as an argument to the functional macro. More...
 

Functions

template<typename Value_set >
std::ostream & flow::cfg::operator<< (std::ostream &os, const Option_set< Value_set > &val)
 Serializes (briefly) an Option_set to a standard output stream. More...
 
template<typename Value >
void flow::cfg::value_to_ostream (std::ostream &os, const Value &val)
 Serializes a value of type Value to the given ostream suitably for output in Option_set-related output to user such as in help messages. More...
 
template<typename Rep , typename Period >
void flow::cfg::value_to_ostream (std::ostream &os, const boost::chrono::duration< Rep, Period > &val)
 Overload that serializes a value of chrono-based duration including Fine_duration – which is recommended to use for Option_set-configured time durations – to the given ostream suitably for output in Option_set-related output to user such as in help messages. More...
 
template<typename Element >
void flow::cfg::value_to_ostream (std::ostream &os, const std::vector< Element > &val)
 Overload that serializes a list value (with Element type itself similarly serializable) to the given ostream suitably for output in Option_set-related output to user such as in help messages. More...
 
template<typename Key >
std::string flow::cfg::value_set_member_id_to_opt_name_keyed (util::String_view member_id, const Key &key)
 Similar to value_set_member_id_to_opt_name() but used by FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED() internally (also made available as a public API in case it is useful), that does the job of value_set_member_id_to_opt_name() in addition to substituting the last [...] fragment with a dot separator, followed by the ostream encoding of key. More...
 

Macro Definition Documentation

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION (   ARG_m_value,
  ARG_description,
  ARG_bool_validate_expr 
)     FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(ARG_m_value, ARG_description, ARG_bool_validate_expr, false)

Macro the user must use, as prescribed in the flow::cfg::Option_set constructor doc header discussing declare_opts_func in their flow::cfg::Option_set::Declare_options_func callback, when declaring each individual config option as linked to a given data member of their config-bearing struct used as the Value_set in Option_set<Value_set>.

Note
The resulting option name, as found in config sources such as config files, will be auto-computed from the string #ARG_m_value. So if ARG_m_value is m_cool_thing.m_cool_guy, then the option name might be computed to be, say, "cool-thing.cool-duration", with a config file line: "cool-thing.cool-duration=2 milliseconds".
It may be desirable to not auto-name a given option; a known use case for this is legacy option names, if renaming is difficult in the field. Use FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED() if indeed you want to custom-name your option.
Parameters
ARG_m_valueSupposing Value_set is the template arg to Option_set, and Value_set V is being registered, then ARG_m_value is such that V.ARG_m_value is the scalar into which the particular config option shall be deserialized. It shall be a scalar data member name, optionally within a compound data member, which is itself optionally within a compound, and so forth, within Value_set V. Each optional compounding may be via simple object composition (.) or pointer dereference (->). Examples: m_direct_member, m_group_obj.m_indirect_member, m_group.m_group1->m_pointed_group->m_cool_scalar. Reminder: each of these must be accessible within a Value_set via . compounding. E.g.: V.m_direct_member a/k/a Value_set::m_direct_member.
ARG_descriptionDescription of the option as shown to the user in help text. Use 1+ sentence(s), starting with capital and ending with punctuator. Indicate all key semantics, including special values, but (1) try to keep to one sentence and line if possible; and (2) save extended discussion for the comment on the data member declaration itself. Omit discussion of defaults; the default will be auto-printed and might change in code later. Reminder: This is a brief summary – not a man page equivalent... but a man page equivalent should be in the actual code.
ARG_bool_validate_exprIn a fashion similar to assert() arg, an expression convertible to bool such that if and only if it evaluates to false at the time of parsing a value for ARG_m_value from a config source, then the value is considered invalid, causing parse failure for that config source. The expression shall assume that the parsed value is in the variable of type Value named val. (If no further validation is needed, you may simply pass in true.)

Definition at line 1157 of file option_set.hpp.

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED (   ARG_m_value,
  ARG_description,
  ARG_bool_validate_expr,
  ARG_key 
)
Value:
( \
const ::std::string FLOW_CFG_SET_DECL_OPT_KEYED_name \
FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED \
(ARG_m_value, FLOW_CFG_SET_DECL_OPT_KEYED_name.c_str(), ARG_description, ARG_bool_validate_expr, false); \
)
std::string value_set_member_id_to_opt_name_keyed(util::String_view member_id, const Key &key)
Similar to value_set_member_id_to_opt_name() but used by FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED() i...
#define FLOW_UTIL_SEMICOLON_SAFE(ARG_func_macro_definition)
Use this to create a semicolon-safe version of a "void" functional macro definition consisting of at ...
Definition: util_fwd.hpp:1079

Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION(), but with support for setting a value at a container subscript.

It works as follows. Normally ARG_m_value, when stringified via # preprocessor trick, looks like a sequence of m_... of identifiers joined with . and/or ->. Call these terms. In this case, however, exactly one of the terms – but not the trailing term – must end with the postfix [...], where ... may be 1 or more characters excluding ]. As in FLOW_CFG_OPTION_SET_DECLARE_OPTION(), the option name will be auto-computed based on the stringification; but with one extra step at the end: [...] shall be replaced by the added macro arg ARG_key, which must be ostream<<able.

This can be used to fill out fixed-key-set containers of structs concisely. Otherwise one would have to tediously unroll the loop and manually provide the name via FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED(). Perhaps best shown by example:

// In Value_set:
constexpr size_t S_N_GRAND_PARENTS = 4;
struct Grand_parent { string m_name; bool m_male_else_female = false; };
array<Grand_parent, S_N_GRAND_PARENTS> m_grand_parents;
// When declaring options for Option_set<Value_set>:
for (size_t idx = 0; idx != Value_set::S_N_GRAND_PARENTS; ++idx)
{
FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED(m_grand_parents[idx].m_name, "The grandparent's name.", true, idx);
FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED(m_grand_parents[idx].m_male_else_female, "Their gender.", true, idx);
}
// Resulting config options:
"grand-parents.0.name"
"grand-parents.0.male-else-female"
"grand-parents.1.name"
"grand-parents.1.male-else-female"
"grand-parents.2.name"
"grand-parents.2.male-else-female"
"grand-parents.3.name"
"grand-parents.3.male-else-female"
// Example file:
[grand-parents.0]
name=Alice Eve
male-else-female=false
[grand-parents.1]
name=Ray Liotta
male-else-female=true
...
#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED(ARG_m_value, ARG_description, ARG_bool_validate_expr, ARG_key)
Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION(), but with support for setting a value at a containe...

Tip: In this example idx is a number; and the container is an array. However, it can be any container with both keys and values; it just needs to always have the same key set after Value_set construction: map, vector, etc. Accordingly the key can be anything ostream<<-able (e.g., string works). However – behavior is undefined if the value, when ostream<<ed, would not be accepted by boost.program_options as an option name postfix. Informally: keep it to alphanumerics and underscores.

Note
Do not confuse this with the situation where one option is itself a vector<T>, where T is a scalar (from boost.program_options' point of view). E.g., a vector<int> can simply be a single option, and the user can supply as many or as few elements as they want, at runtime (in config file, one option per line). For example the above Value_set can itself contain a vector<> in each struct in the array:
// In Value_set:
struct Grand_parent { ...; vector<int> m_kid_ages; };
// When declaring options for Option_set<Value_set>:
for (size_t idx = 0; idx != Value_set::S_N_GRAND_PARENTS; ++idx)
{
...
FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED(m_grand_parents[idx].m_kid_ages, "Children's age list.", true, idx);
}
// Resulting config options:
...
"grand-parents.0.kid-ages"
...
"grand-parents.1.kid-ages"
...
"grand-parents.2.kid-ages"
...
"grand-parents.3.kid-ages"
// Example file:
[grand-parents.0]
name=Alice Eve
male-else-female=false
kid-ages=5
kid-ages=7
kid-ages=25
[grand-parents.1]
name=Ray Liotta
male-else-female=true
kid-ages=32
...
Parameters
ARG_m_valueSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_descriptionSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_bool_validate_exprSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_keySee above.

Definition at line 1277 of file option_set.hpp.

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED_NO_ACC

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED_NO_ACC (   ARG_m_value,
  ARG_description,
  ARG_bool_validate_expr,
  ARG_key 
)
Value:
( \
const ::std::string FLOW_CFG_SET_DECL_OPT_KEYED_name \
FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED \
(ARG_m_value, FLOW_CFG_SET_DECL_OPT_KEYED_name.c_str(), ARG_description, ARG_bool_validate_expr, true); \
)

Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED_NO_ACC(), except the option is marked as non-accumulating in the same sense as for FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC().

See also
FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC() for a brief explanation of how non-accumulating options work.
Parameters
ARG_m_valueSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_descriptionSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_bool_validate_exprSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_keySee FLOW_CFG_OPTION_SET_DECLARE_OPTION_KEYED().

Definition at line 1301 of file option_set.hpp.

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED (   ARG_m_value,
  ARG_opt_name_c_str,
  ARG_description,
  ARG_bool_validate_expr,
  ARG_no_accumulation 
)

Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(), except the user must specify the option's string name manually as an argument to the functional macro.

(FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS() itself is basically FLOW_CFG_OPTION_SET_DECLARE_OPTION() plus certain knobs which the former sets to commonly-used values for concision.)

This macro, which can be used directly but normally is not, is internally invoked (potentially indirectly) by any other FLOW_CFG_OPTION_SET_DECLARE_OPTION*() macro. As such it is the essential core such macro.

Informally: it is best to avoid its direct use, as it can break the auto-naming conventions maintained by FLOW_CFG_OPTION_SET_DECLARE_OPTION() and similar. That said a couple of use cases might be:

Parameters
ARG_opt_name_c_strThe option's manually specified string name. Type: directly convertible to const char*; typically a string literal.
ARG_m_valueSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_descriptionSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_bool_validate_exprSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_no_accumulationSee FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS().

Definition at line 1369 of file option_set.hpp.

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC (   ARG_m_value,
  ARG_description,
  ARG_bool_validate_expr 
)     FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS(ARG_m_value, ARG_description, ARG_bool_validate_expr, true)

Identical to FLOW_CFG_OPTION_SET_DECLARE_OPTION(), except the option is marked as non-accumulating, which means that each time a config source (such as file) is parsed, this option's value is reset to default and then only overwritten with a potential non-default value if explicitly specified in the config source.

(Otherwise the option is accumulating, hence whatever value is already stored in the Value_set is left unchanged unless specified in the config source.)

Note
This may be useful, in particular, when used in flow::cfg::Config_manager in multi-file updates for special options that implement conditionality. It does not make sense to accumulate such options' values from file to file in a given update, so this _NO_ACC variant macro makes sense to use.
Parameters
ARG_m_valueSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_descriptionSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_bool_validate_exprSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().

Definition at line 1178 of file option_set.hpp.

◆ FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS

#define FLOW_CFG_OPTION_SET_DECLARE_OPTION_WITH_KNOBS (   ARG_m_value,
  ARG_description,
  ARG_bool_validate_expr,
  ARG_no_accumulation 
)
Value:
( \
const ::std::string FLOW_CFG_SET_DECL_OPT_name = ::flow::cfg::value_set_member_id_to_opt_name(#ARG_m_value); \
FLOW_CFG_OPTION_SET_DECLARE_OPTION_MANUALLY_NAMED \
(ARG_m_value, FLOW_CFG_SET_DECL_OPT_name.c_str(), ARG_description, ARG_bool_validate_expr, \
ARG_no_accumulation); \
)
std::string value_set_member_id_to_opt_name(util::String_view member_id)
Utility, used by FLOW_CFG_OPTION_SET_DECLARE_OPTION() internally but made available as a public API i...
Definition: option_set.cpp:38

As of this writing is identical to either FLOW_CFG_OPTION_SET_DECLARE_OPTION() or FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC(), depending on the value of ARG_no_accumulation argument.

Rationale: Currently we don't necessarily expect this to be widely used directly in place of the aforementioned two macros, but it seems reasonable to have a single macro with all various knobs given as parameters with certain knob combinations potentially invoked via macro(s) based on this one. As of this writing there is just the one extra knob, ARG_no_accumulation, but in the future we might expand to more variations in which case more args would be added here.

Parameters
ARG_m_valueSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_descriptionSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_bool_validate_exprSee FLOW_CFG_OPTION_SET_DECLARE_OPTION().
ARG_no_accumulationbool such that false causes FLOW_CFG_OPTION_SET_DECLARE_OPTION() behavior, while true causes FLOW_CFG_OPTION_SET_DECLARE_OPTION_NO_ACC() behavior.

Definition at line 1330 of file option_set.hpp.