Reputation: 195
What I currently have is
("someOption,s", po::bool_switch(&variable)->default_value(false), "")
I would like to be able to specify the argument multiple times and each occurence would switch the value of this bool.
Examples:
default value = false
./program -s
value == true
./program -s -s
value == false
./program -s -s -s
value == true
Is there a way to use something like bool_switch multiple times so switch on/off repeatedly? Do I need my custom type and validator for that?
Maybe I can somehow allow specifying the option multiple times and then do vm.count("someOption") and based on its value (even/odd) switch my variable. But I would prefer to specify that behaviour right in the options descripton (add_options) without checking and modifying the values later.
Upvotes: 2
Views: 1268
Reputation: 195
So this is as close I got so far. It does not look very elegant, more like a workaround, I would be happy to know better ("shorter") solution. There could be also some problems with the code, feel free to correct me please. Currently it works only if default is treated as false. I don't know how to start with true, it would require to know the default value somehow in validate function. It is out of scope of this questions and hopefully solved here: boost::program_options custom validate and default value
// my custom class
class BoolOption {
public:
BoolOption(bool initialState = false) : state(initialState) {}
bool getState() const {return state;}
void switchState() {state = !state;}
private:
bool state;
};
// two variables
BoolOption test1;
BoolOption test2;
// validate
void validate(boost::any &v, std::vector<std::string> const &xs, BoolOption*, long)
{
if (v.empty()) {
v = BoolOption(true); // I don't know how to assign default here so this works only when default is false
} else {
boost::any_cast<BoolOption&>(v).switchState();
}
}
optionsDescription->add_options()
("test1,t", po::value<BoolOption>(&test1)->default_value(BoolOption(true), "true")->zero_tokens(), "")
("test2,T", po::value<BoolOption>(&test2)->default_value(BoolOption(false), "false")->zero_tokens(), "")
;
// output result
cout << test1.getState() << endl;
cout << test2.getState() << endl;
This seems to work correctly, I can call it in a way:
./program -t -t -T -t -T
and it switches the arguments.
Upvotes: 1
Reputation: 393174
I've literally spent 70 minutes trying to get this to work using
composing()
to allow repeatsimplicit_value({}, "")
custom notifiers (they only run once)notifier()
- they only run once regardless of how often the option is present and successfully parsedvariables_map
after store
/notify
. Sadly, the size is always 1, presumably because the "compose" doesn't actually compose within a single store operation (it only composes between several runs, so different sources of options, then?).The sad conclusion is, there doesn't appear to be a way. Here, my usual mantra is confirmed: "Simplicity Trumps Omniscient Design", and I'd suggest doing the same but using https://github.com/adishavit/argh:
#include "argh.h"
#include <iostream>
namespace {
template <typename It>
size_t size(std::pair<It, It> const& range) { return std::distance(range.first, range.second); }
}
int main(int argc, char** argv) {
argh::parser p(argc, argv);
auto num_s = size(p.flags().equal_range("s"));
bool const variable = num_s % 2;
std::cout << "Repeated: " << num_s << ", effective " << std::boolalpha << variable;
std::cout << " (Command line was:";
while (*argv) std::cout << " " << *argv++;
std::cout << ")\n";
}
When run with various commandlines, prints:
Repeated: 0, effective false (Command line was: ../build/sotest)
Repeated: 1, effective true (Command line was: ../build/sotest -s)
Repeated: 2, effective false (Command line was: ../build/sotest -s -s)
Repeated: 3, effective true (Command line was: ../build/sotest -s -s -s)
Repeated: 4, effective false (Command line was: ../build/sotest -s -s -s -s)
Repeated: 3, effective true (Command line was: ../build/sotest -s -s -s --other bogus)
Upvotes: 4