My program has a set of recoverable errors that can occur, let's call them ErrorA
, ErrorB
and ErrorC
. For any of these errors, my program can either ignore them (as they are recoverable), or terminate, the latter being the default behaviour. Now I want users to specify if any of these errors should be ignored instead. This should happen through a command line parameter --ignore
, which is followed by the names of the errors that shall be ignored. For example:
./MyProgram --ignore ErrorA ErrorC
Within the code, I represent these error categories using a single bitmask enum:
enum class IgnoreErrors {
None = 0, //Ignore no errors
ErrorA = 1 << 0,
ErrorB = 1 << 1,
ErrorC = 1 << 2
};
I am using boost::program_options
and trying to add the --ignore
option to the options_description
like so:
value<IgnoreErrors>()->multitoken()->default_value(IgnoreErrors::None)
This requires that I provide an implementation of operator>>
for IgnoreErrors
, and this is where I got stuck. I tried this:
istream& operator>>(istream& in, IgnoreErrors& err) {
//Assumption: 'in' contains ALL tokens passed to the --ignore flag
err = IgnoreErrors::None; //Needed to make |= work
string token;
while(getline(in, token, ' ')) {
err |= ignore_error_from_str(token);
}
return in;
}
This correctly parses one or more tokens into a single IgnoreErrors
value, however after that my program fails with: the argument ('ErrorA ErrorC') for option '--ignore' is invalid
. Interestingly enough, if I do in >> token
instead of getline
, I only ever get the first token (ErrorA
).
If I change the implementation of operator>>
to parse a single token into a single IgnoreError
value and change my options_description
to use value<vector<IgnoreErrors>>()
, Boost doesn't recognize the operator>>
for IgnoreErrors
anymore and instead looks for an implementation for vector<IgnoreErrors>
. Which I could provide, however then I have to manually fold this vector into a single IgnoreErrors
value, which seems weird. I want to understand what the 'intended' way to handle this situation is.
How can I parse a multitoken argument as a single value using boost::program_options
?
So I found a way to make this work that seems reasonable. Instead of providing operator>>
, a custom validator can be used, like so:
void validate(boost::any &v, const std::vector<std::string> &all_tokens,
IgnoreErrors *target_type, int) {
validators::check_first_occurrence(v);
std::vector<std::string> tokens;
boost::algorithm::split(tokens,
validators::get_single_string(all_tokens),
boost::is_any_of(" "));
auto ignore_errors = IgnoreErrors::None;
for (auto &token : tokens) {
ignore_errors |= ignore_error_from_str(token);
}
v = boost::any(ignore_errors);
}
Some things that were not so obvious to me:
validators::get_single_string(all_tokens)
in my example is "ErrorA ErrorC"
, so I had to split this string up into multiple tokens manuallyvalidate
function has to be in the same namespace as the type that it is specialized for (IgnoreErrors
in my case)