4

さて、私がc ++で何か大きなものを書いてからしばらく経ち、より現代的な言語の優れた点のいくつかに慣れてきました。これは私を悩ませてきたものであり、そこに答えがあると確信しています。実行時にユーザーが文字列として指定した関数を呼び出す方法はありますか?ある種の大規模なスイッチ/ifブロックに頼る必要はありませんか?

私が直面している状況は、要約すると、C ++で解決し、「Problem1.cpp / Problem1.h」、「Problem2.cpp / Problem2」と指定した、数学関連の問題が大量に発生しています。 .h "など。各問題には、解決策を開始する(問題の番号は)という関数がありますproblemX()Xプログラムの開始時に、「どの問題を解決したいですか?」とユーザーに尋ねたいと思います。そして彼らは数を指定するでしょう。problemX()次に、ハードコードされた大規模なswitchステートメント(またはifステートメント、関数ポインターのインデックス付き配列など)を使用せずに、適切な関数を呼び出したいと思います。

これは可能になっていると思いますが、どうすればいいのか思い出せません。何か案は?

4

3 に答える 3

5

unordered_map関数ポインタへの文字列の変換。

ユーザー入力を微調整して、すべて小文字(または叫びたい場合は大文字)になるようにしてから、関数を検索します。存在する場合はそれを呼び出し、そうでない場合はエラーを呼び出します。

于 2012-11-02T20:06:00.643 に答える
1

C ++には、その言語でのコードの自動コンパイルまたは実行時の反映はありません。多くのライブラリフレームワークには、ライブラリ内のシンボルの実行時の反映があります。

したがって、解決策1:問題を独自のダイナミックライブラリに貼り付け、メインプログラムに動的にロードさせ、エクスポートするシンボル名を検索させます。

解決策2:生のCスタイルの関数を名前付きオブジェクトに置き換えます。だからあなたは持っているかもしれません:

class Problem;
void RegisterProblem( std::string name, Problem const* problem );
std::map< std::string, Problem const* >& GetProblems();
class Problem
{
protected:
  Problem( std::string name ): RegisterProblem( std::move(name), this ) {}
  virtual void operator() const = 0;
  virtual ~Problem() {}
};
class Problem1: public Problem
{
public:
  Problem1():Problem("Problem1") {}
  virtual void operator() const { /* implementation */ }
};

// in .cpp file:
Problem1 problem1Instance();


void RegisterProblem( std::string name, Problem const* problem )
{
  GetProblems()[name] = problem;
}

std::map< std::string, Problem const* >& GetProblems()
{
  static std::map< std::string, Problem const* > problemMap;
  return problemMap;
}

int main()
{
  // parse user input to get this string:
  std::string testInput = "Problem1";

  // run the problem determined by testInput:
  Problem* prob = GetProblems()[testInput];
  Assert(prob);
  (*prob)();
}

上記には、自己登録の問題(静的マップに登録する)と、文字列で指定された問題を実行するmain()が含まれる、ひどく書かれたコードがいくつかあります。

私がよりクリーンになると思う方法は次のとおりです。

// In RegisterProblem.h:
// these two have obvious implementations:
std::map< std::string, std::function<void()> >& GetProblems(); 
bool RegisterProblem( std::string s, std::function<void()> ); // always returns true

// In problem1.cpp:
void Problem1(); // implement this!
bool bProblem1Registered = RegisterProblem( "Problem1", Problem1 );
// In problem2.cpp:
void Problem2(); // implement this!
bool bProblem2Registered = RegisterProblem( "Problem2", Problem2 );

// etc

// in main.cpp:
int main(int argc, char** argv)
{
  if (argc == 0)
    return -1; // and maybe print help
  auto it = GetProblems().find( argv[1] );
  if (it == GetProblems().end())
    return -1; // and maybe print help
  it->second(); // call the problem
}

ここでは、不要なクラス階層を排除し、文字列とvoid()関数の間のマップを維持するだけです。このマップの保守は、関数が記述されている各場所に分散されているため、中央のリストやifステートメントはありません。

上記のような粗雑なコードで何も出荷しませんが、あなたがアイデアを理解してくれることを願っています。

于 2012-11-02T20:23:16.660 に答える
0

std::map<std::string,_function_pointer_defined_by_you>関数の名前をキーとして、関数ポインタを値として格納するには、を使用する必要があります。std::unordered_map<std::string,_function_pointer_defined_by_you>のようなものを使用することもできますstd::hash_map。C ++ 11を使用できる場合はstd::unordered_map、ヘッダーファイル<unordered_map>にあります。使用できない場合は、にあります<tr1/unordered_map>。mapとunordered_mapの両方に関するドキュメントは、次の場所にあります。

于 2012-11-02T20:17:55.540 に答える