【发布时间】:2013-01-09 20:24:45
【问题描述】:
我偶然发现了标题中定义的问题。我有一个创建options_description 实例的应用程序,然后在其上使用add_options()。
很像示例中的:
options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;
我的问题是,如何在调用后修改optimization 的默认值。这甚至可能吗?文档对我来说似乎很模糊。据我了解,这个问题可以概括为任何值语义,因为value_semantic 是括号中的第二个参数。
动机
我觉得这可能是不可能的。所以我想介绍一下我对这种功能的动机。也许我的意图设计有缺陷,所以你可以提出其他建议。
我有几个程序执行非常相似的任务并共享相当多的参数和开关。我想我可以将公共参数重构为一个单独的基类。我虽然可以以类似的方式重构命令行解析。 boost::program_options 很符合我的想法。我将options_description 实例构造为基类中的私有属性,并在那里添加常用选项。然后在初始化时的派生类中,我再次在该对象上执行add_options(),添加更多特定选项。这看起来很整洁,而且我很快就可以使用它。
然后我注意到所有派生类都有一个通用选项,但如果它有一个不同的默认值会非常好。 IE。输出文件的名称。让 app1 成为 app1.out、app2 - app2.out 等。
当然,我可以在派生类中将输出文件名选项移动到add_options,但这似乎很愚蠢和多余,因为即使在语义上,除了默认值之外,一切都是一样的。另一种解决方法是从基类中的默认值和派生类的解析后步骤中退出,检查该选项是否已设置并手动应用(默认)值。然而,这似乎也是多余的,因为预期的功能似乎是在库本身中实现的。
我将尝试提供一个代码示例,以便您稍后或根据要求更好地感受它。不过,我认为我的方法很明确。
编辑 - 代码示例 它是在 Rob 的回答之后写的,所以我尽量保持在命名约定范围内。
Base - 执行解析并允许将优化级别设置为整数:
#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;
}
高度优化的版本,允许选择性地执行花哨的东西:
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;
}
允许开启详细调试的非优化版本:
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;
}
我尝试垂直压缩它,但无论如何它都变长了 =/.对不起,如果我过火了。反过来,这些例子是清晰的和独立的。
BaseClass 几乎可以设置所有内容并解析常见内容。派生类在构造函数和重载解析中添加自己的选项。他们确实执行基本解析器并检查错误。这也使--help 工作。
现在的事情是修改每个派生的优化的默认值。因为如果将 NonOptimizedClass 设置得非常低,OptimizedClass 设置得非常高,那就太好了。
【问题讨论】:
标签: c++ boost boost-program-options