I have stumbled upon problem defined in the title. I have an application that creates an instance of options_description
then uses add_options()
on it.
Pretty much like in the example:
options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;
My question is, how could one modify the default value for the optimization
after this call. Is this even possible? The documentation seems pretty murky to me. From what I understand, the question can be generalized to any value semantic, as value_semantic
is the 2nd parameter in the parentheses.
Motivation
I get the feeling this might be not possible. So I would like to present my motivation for such functionality. Maybe my intented design is flawed, so you can suggest something else.
I have several programs that perform quite similar tasks and share quite some parameters and switches. I thought I can refactor the common parameters to a separate base class. I though that I can refactor the command line parsing along in similar manner. boost::program_options
do work quite well with my idea. I construct options_description
instance as a private property in the base class and add common options there. Then in derived classes upon initialization I execute add_options()
on this object again adding more specific options. This seemed quite neat and I got it working pretty fast.
Then I noticed that all of the derived classes would have a common option, but it would be really nice for it to have a different default value. I.e. name of the output file. For app1 to be app1.out, app2 - app2.out etc.
Of course I can move the output filename option to add_options
in the derived classes but it seems stupid and redundand since even semantically, everything is the same except the default value. The other workaround would be to resign from default value in the base class and in the after-parse step in derived classes, check whether the option was set and apply a (the default) value manually. However, this also seems redundant since the intended functionality seems to be implemented in the library itself.
I will try to provide a code example so you can feel it better later on or upon a request. Though, I think my approach is pretty clear.
EDIT - The code examples
It was written after Rob's answer so I tried to stay within the naming convention.
Base - performs parsing and allows to set optimization level as integer:
#include <boost/program_options.hpp>
namespace po = boost::program_options;
class BaseClass {
public:
BaseClass::BaseClass();
virtual int parse(const int argc, char** argv);
private:
po::options_description m_desc;
po::variables_map vm;
int optimization_level;
};
BaseClass::BaseClass():
m_desc()
{
m_desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;
}
int BaseClass::parse(const int argc, char** argv)
{
po::store(po::parse_command_line(argc, argv, desc), vm);
po::notify(vm);
if (vm.count("help")) { std::cout << desc << "\n"; return 1; }
optimization_level = vm["optimization"].as<int>();
return 0;
}
Highly optimized version which allows to optionally perform fancy stuff:
class HighlyOptimizedClass : public BaseClass {
public:
HighlyOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
bool fancy_optimizations;
};
HighlyOptimizedClass(): BaseClass() {
m_desc.add_options()
("fancy,f", po::value<bool>()->zero_tokens(), "perform fancy optimizations")
;
}
HighlyOptimizedClass::parse(const int argc, char** argv)
{
int ret = BaseClass::parse(argc, argv); //execute base function
if( ret ) return ret; //return if it didnt succed
if ( vm.count("fancy") ) fancy_optimizations = 1; // non-base stuff
return 0;
}
Non-optimized version which allows to turn on verbose debugging:
class NonOptimizedClass : public BaseClass {
public:
NonOptimizedClass();
virtual int parse(const int argc, char** argv);
private:
bool verbose_debug;
};
NonOptimizedClass(): BaseClass() {
m_desc.add_options()
("verbose,v", po::value<bool>()->zero_tokens(), "genrates TONS of output")
;
}
NonOptimizedClass::parse(const int argc, char** argv)
{
int ret = BaseClass::parse(argc, argv); // execute base function
if( ret ) return ret; // return if it didnt succed
if ( vm.count("verbose") ) verbose_debug = 1; // non-base stuff
return 0;
}
I tried to compress it vertically but it got long anyways =/. Sorry if I went overboard. In turn, the examples are clear and self contained.
The BaseClass
sets up pretty much everything and parses common stuff. The derived classes add their own options in constructors and overload parsing. They do execute the base parser and check for errors. This also makes --help
work.
Now the thing is to modify the default value of optimizations for each of the derives. As it would be nice to have it set really low for NonOptimizedClass
and really high for OptimizedClass
.