Flow 1.0.0
Flow project: Public API.
|
We may add some ADL-based overloads into this namespace outside flow
.
More...
Functions | |
void | validate (boost::any &target, const std::vector< std::string > &user_values, path *, int) |
ADL-based overload of boost.program_options validate() to allow for empty boost::filesystem::path values in flow::cfg config parsing as well as any other boost.program_options parsing in the application. More... | |
We may add some ADL-based overloads into this namespace outside flow
.
void boost::filesystem::validate | ( | boost::any & | target, |
const std::vector< std::string > & | user_values, | ||
path * | , | ||
int | |||
) |
ADL-based overload of boost.program_options validate()
to allow for empty boost::filesystem::path
values in flow::cfg config parsing as well as any other boost.program_options parsing in the application.
Likely there's no need to call this directly: it is invoked by boost.program_options when parsing path
s.
More precisely: This has ~two effects:
Option_set
) will successfully parse an empty (or all-spaces) value for a setting of type boost::filesystem::path
, resulting in a path
equal to a default-constructed path()
.path
will now also allow an empty value (on command line, in file, etc.) in the same application.Allowing empty path
s in flow::cfg is required for usability. It was a common use case to allow for a blank special value for path settings. However trying to in fact provide an empty value in a flow::cfg file (e.g., simply log-file=
) resulted in an "Invalid argument" error message and refusal of flow::cfg::Option_set::parse_config_file() to successfully parse.
(The following discussion is about implementation details and would normally be omitted from this public-facing API doc header. However, in this slightly unusual (for Flow) situation the solution happens to subtly affect code outside of flow::cfg. Therefore it is appropriate to discuss these internals here.)
The reason for this problem: flow::cfg uses boost.program_options for config source (especially file) parsing. By default, when parsing a string into the proper type T
(here T
being path
), boost.program_options uses istream >> T
overload. Turns out that reading a blank from a stream into path
causes the istream
bad-bit to set, meaning lexical_cast
throws bad_lexical_cast
; boost.program_options then manifests this as an arg parse error. (If T
were std::string
, by contrast, no such problem would occur: istream >> string
will happily accept a blank string.)
boost.program_options clearly suggests the proper way to custom-parse types at https://www.boost.org/doc/libs/1_78_0/doc/html/program_options/howto.html#id-1.3.32.6.7 – namely, define a validate()
overload in the same namespace as type T
, with a T
-typed arg. ADL (argument-dependent lookup) will then use that overload – instead of the default one, which simply invokes lexical_cast
(i.e., istream>>
) – and reports an error if that throws. This is the same technique used to conveniently select hash_value()
for unordered_*
; swap()
for STL containers; etc.
This solves the problem. However, somewhat unusually, this imposes the same more-permissive semantics on all other uses of boost.program_options in any application linking Flow (which includes this overload). This is arguably not-great; ideally we'd affect flow::cfg and nothing else. That said (1) there's no apparent alternative in boost.program_options; and more to the point (2) this feels like either an improvement or neutral. If an empty path must be disallowed, this can be done via notifier()
(or just a manual check). So, all in all, this seemed fine.
target | target shall be loaded with the path on success. Otherwise we shall throw, per required boost.program_options semantics. |
user_values | The (trimmed) raw strings from the user for this occurrence of the setting. In our case there must be only one, since a path has only one value. |