This question has been already been asked here before - Boost parse_config_file, empty key value. But since there was no proper solution provided there, I am asking this again hoping someone could provide a better solution.
Unlike the above question, In my case, the code is not throwing the Boost Error:
boost::program_options::invalid_option_value
Instead, for string options
, it sets the value to an empty string("")
and for bool
, it is set to true
. The intention of my code is to identify whether some option is set in the config file or not. I was hoping to test this using vm[optionName].count()
(where vm is the variables_map object) but in the case where the value is not specified like option=
, this returns true and hence cannot be used.
I also tried - vm[optionName].defaulted()
, vm[optionName].empty()
and implicit_value()
and default_value()
while adding the options but none of them worked.
UPDATE Initial answer got the wrong idea. Here's the update.
So you want foo=
(without a value) to behave as if the line wasn't even in the config.
That means that the default value semantics (i.e. what happens when notifying - which migrates state from the Parser Component to the Storage Component) aren't good.
You could pave over it by inventing your own value-semantics (mybool_switch, so to speak) and or settling for a value<my_particulat_bool>
where you add streaming operations so that the option behaves the way you want. In other words, using a canon to shoot a fly.
However, by far the simpler option would be to interfere at the parser stage, changing the parsed_options
before you notify()
.
Here's a rather complete illustration with a live demo:
#include <boost/program_options/config.hpp>
#include <boost/program_options.hpp>
#include <iostream>
#include <iomanip>
namespace po = boost::program_options;
int main() {
po::options_description desc;
desc.add_options()
("foo", po::bool_switch())
("bar", po::bool_switch()->default_value(false))
("qux", po::bool_switch()->implicit_value(false))
;
std::set<std::string> const bool_switches {"foo", "bar", "qux" };
for (std::string contents :
{ "", "foo=", "foo=true",
"bar=", "bar=true",
"qux=", "qux=true"})
{
std::istringstream iss(contents);
po::parsed_options parsed = po::parse_config_file(iss, desc, false);
std::cout << "\n---\n" << std::quoted(contents) << "\n";
// the magic is here:
for (auto it = parsed.options.begin(); it!= parsed.options.end();) {
using V = std::vector<std::string>;
V const& v = it->value;
if (bool_switches.count(it->string_key) && (v==V{} || v==V{""})) {
std::cout << "*** Discarding config key without a value: " << it->string_key << "\n";
it = parsed.options.erase(it);
} else {
++it;
}
}
po::variables_map vm;
po::store(parsed, vm);
for (auto& key : bool_switches) {
auto& entry = vm[key];
std::cout << " " << key << " ->" << std::boolalpha
<< (entry.empty()?" .empty()":"")
<< (entry.defaulted()?" .defaulted()":"");
if (entry.empty())
std::cout << " (no value)\n";
else
std::cout << " value:" << entry.as<bool>() << "\n";
}
}
}
Which will print
---
""
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo="
*** Discarding config key without a value: foo
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"foo=true"
bar -> .defaulted() value:false
foo -> value:true
qux -> .defaulted() value:false
---
"bar="
*** Discarding config key without a value: bar
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"bar=true"
bar -> value:true
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux="
*** Discarding config key without a value: qux
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> .defaulted() value:false
---
"qux=true"
bar -> .defaulted() value:false
foo -> .defaulted() value:false
qux -> value:true