2

我偶然发现了标题中定义的问题。我有一个应用程序,它创建一个options_descriptionthen 使用的实例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()再次在这个对象上执行添加更多特定选项。这看起来很整洁,我让它工作得很快。

然后我注意到所有派生类都有一个通用选项,但是如果它有一个不同的默认值,那就太好了。即输出文件的名称。让 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

4

3 回答 3

5

您可以调用options_description::find("optimization", ...)以获取对关联的引用option_description,其semantic方法将为您提供指向value_semantic您在调用时最初提供的 的指针add_options。但是,它是一个 const 指针,因此您似乎不允许修改它指向的内容。

但是,value_semantic当您创建它时不是 const,这意味着使用它应该可以安全地const_cast删除option_description适用的 const 限定。您还必须将value_semantic对象类型转换回typed_value您最初调用时获得的正确类型value<T>

option_description const& optimization = desc.find("optimization", false);
shared_ptr<const value_semantic> cvalue = optimization.semantic();
shared_ptr<value_semantic> value = const_pointer_cast<value_semantic>(cvalue);
shared_ptr<typed_value<int>> tvalue = dynamic_pointer_cast<typed_value<int>>(value);
assert(tvalue);
tvalue->default_value(20);

另一种设计可以避免在定义选项后修改选项(这显然不是program_options设计要做的事情),是让程序特定的派生类将所需的默认值传递给基类。然后基类可以在定义优化选项时使用该值。

BaseClass::BaseClass(int default_optimization):
  m_desc()
{
  m_desc.add_options()
    ("help",
      "produce help")
    ("optimization",
      value<int>()->default_value(default_optimization),
      "optimization level")
    ;
}

HighlyOptimizedClass::HighlyOptimizedClass():
  BaseClass(99)
{ }

NonOptimizedClass::NonOptimizedClass():
  BaseClass(0)
{ }
于 2013-01-09T22:11:34.783 回答
0

value_semantic好的,所以我发现在当前版本中,在不违反program_optionsby const_casting的现有设计的情况下无法更改。

在阅读了 Rob Kennedy 和 Yakk 的回答和建议后,我想出了一种将两者结合起来的方法。它应该像描述的那样工作,并且不应该不必要地混乱任何东西。

这个想法是添加旨在在单独调用中更改的选项。使其成为虚拟并在基类中定义默认情况。

这种方法允许program_option一次性定制整体,而不仅仅是单一语义。为每个可能更改的单个案例添加参数或方法对我来说似乎真的很麻烦。

更改后的代码如下所示:

根据

class BaseClass {
public:
  BaseClass::BaseClass();
  virtual int parse(const int argc, char** argv);
private:
  virtual void add_optimization_option();
  po::options_description m_desc;
  po::variables_map vm;
  int optimization_level;
};

BaseClass::BaseClass(): m_desc() {
  m_desc.add_options()("help", "produce help");
}

void BaseClass::add_optimization_option(){
 m_desc.add_options()
  ("optimization", value<int>()->default_value(10), "optimization level");
}

优化版:

class HighlyOptimizedClass : public BaseClass {
public:
  HighlyOptimizedClass();
  virtual int parse(const int argc, char** argv);
private:
  virtual void add_optimization_option();
  bool fancy_optimizations;
};

void HighlyOptimizedClass::add_optimization_option(){
  m_desc.add_options()
   ("optimization", value<int>()->default_value(99), "optimization level");
}

未优化:

class NonOptimizedClass : public BaseClass {
public:
  NonOptimizedClass();
  virtual int parse(const int argc, char** argv);
private:
  virtual void add_optimization_option();
  bool verbose_debug;
};

void NonOptimizedClass::add_optimization_option(){
  m_desc.add_options()
   ("optimization", value<int>()->default_value(0), "optimization level");
}

代价是为每个修改的选项添加一个私有方法,并为我们想要修改它的每个案例重载一个私有方法。当一个人想要让它保持默认时,什么都不需要。如果可以修改,value_semantic则可以避免定义新方法。然而,除了这个障碍,它运作良好并且不会弄乱其他任何东西。

于 2013-01-10T11:13:32.240 回答
0

const std::vector< shared_ptr< option_description > > & options_description::options() const;为您提供对option_description.

Aconst shared_ptr<T>不是shared_ptr<const T>.

但是,如果我遇到这种问题,我会改用add_output_file( std::string default ), 并让它调用。add_option(很可能,尽管从界面上看上述内容似乎是合法的,但像这样弄乱内部结构可能会使boost图书馆感到困惑。

于 2013-01-09T21:21:52.163 回答