2

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.

abhishek gupta
  • 359
  • 1
  • 12

1 Answers1

1

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:

Live On Coliru

#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
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Gah, Reading your question again makes me wonder whether this was actually your problem (it helps if you are explicit). Maybe you were after this: http://coliru.stacked-crooked.com/a/4ccb762029649112 – sehe May 11 '20 at 18:35
  • If that is ***not*** it, then no doubt, you need to manipulate `parsed_options` intermediate before notifying. [Here's a good primer](https://stackoverflow.com/a/32260465/85371) on the interface: – sehe May 11 '20 at 18:44
  • Made that work **[Live On Coliru](http://coliru.stacked-crooked.com/a/76c09c1a09303e0f)**. Let me know if that was the answer, I can update the text to reflect it. Be sure to read the previously linked answer on the rationale for this solution. – sehe May 11 '20 at 18:47
  • Iterating over the parsed options before storing them seems to be a really good solution for this. Thank you very much :). Just wanted to confirm that using bool_switch(),default_value() and implicit_value() are not relevant to the solution. – abhishek gupta May 11 '20 at 20:03
  • Yeah. My take on these things is to cover all options, because the question didn't specify and it was already tricky to get the exact goal. Glad to be of help. Updated (replaced) the answer. – sehe May 11 '20 at 20:17