2

タイトルで定義されている問題に遭遇しました。のインスタンスを作成し、options_descriptionそれを使用するアプリケーションがありますadd_options()。例のように:

options_description desc;
desc.add_options()
("help", "produce help")
("optimization", value<int>()->default_value(10), "optimization level")
;

optimization私の質問は、この呼び出しの後のデフォルト値をどのように変更できるかということです。これも可能ですか?ドキュメントは私にはかなり曖昧に見えます。私が理解していることから、質問はvalue_semantic、括弧内の2番目のパラメーターと同様に、任意の値のセマンティックに一般化できます。

動機

これは不可能かもしれないと感じます。そこで、そのような機能に対する私の動機を示したいと思います。たぶん私の意図したデザインに欠陥があるので、何か他のものを提案することができます。

非常によく似たタスクを実行し、かなりの数のパラメーターとスイッチを共有するプログラムがいくつかあります。共通パラメーターを別の基本クラスにリファクタリングできると思いました。私は、同様の方法でコマンドライン解析をリファクタリングできると思います。boost::program_options私の考えでは非常にうまく機能します。options_description 基本クラスのプライベートプロパティとしてインスタンスを構築し、そこに共通のオプションを追加します。次に、初期化時に派生クラスadd_options()で、このオブジェクトを再度実行して、より具体的なオプションを追加します。これは非常にきれいに見え、私はそれをかなり速く動作させました。

次に、すべての派生クラスに共通のオプションがあることに気付きましたが、デフォルト値が異なると非常に便利です。つまり、出力ファイルの名前。app1がapp1.out、app2-app2.outなどになるようにします。

もちろん、出力ファイル名オプションをadd_options派生クラスに移動することはできますが、意味的にもデフォルト値を除いてすべて同じであるため、愚かで冗長なようです。もう1つの回避策は、基本クラスのデフォルト値を辞任し、派生クラスの解析後の手順で、オプションが設定されているかどうかを確認し、(デフォルトの)値を手動で適用することです。ただし、意図した機能がライブラリ自体に実装されているように見えるため、これも冗長に見えます。

コード例を提供して、後でまたは要求に応じてより良く感じることができるようにします。しかし、私のアプローチはかなり明確だと思います。

編集-コード例 それはロブの答えの後に書かれたので、私は命名規則の範囲内にとどまろうとしました。

ベース-解析を実行し、最適化レベルを整数として設定できます。

#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へのポインタを提供します。ただし、これはconstポインターであるため、それが指すものを変更することは許可されていないようです。value_semanticadd_options

ただし、value_semantic作成したときはconstではなかったため、適用されるconst_castconst資格を削除するために安全に使用できるはずです。また、オブジェクトを最初に呼び出したときに取得した正しい型option_descriptionに型変換する必要があります。value_semantictyped_valuevalue<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

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 に答える
0

さて、現在のバージョンでは、 ingによってvalue_semantic既存の設計に違反せずにを変更することはできないことがわかりました。program_optionsconst_cast

ロブ・ケネディとヤックの答えと提案を読んだ後、私はこの2つを組み合わせたアプローチを思いつきました。説明どおりに機能する必要があり、不必要に何かを乱雑にしないでください。

別の呼び出しで変更することを目的としたオプションを追加するという考え方です。それを仮想化し、基本クラスでデフォルトのケースを定義します。

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");
}

コストは、変更されたオプションごとに1つのプライベートメソッドを追加し、変更するケースごとに1つのプライベートメソッドをオーバーロードすることです。デフォルトのままにしておきたい場合は、何も必要ありません。変更value_semanticが可能であれば、新しいメソッドの定義を回避できます。それにもかかわらず、この障害を除いて、それはうまく機能し、他のものを乱雑にすることはありません。

于 2013-01-10T11:13:32.240 に答える