4

好的,自从我用 C++ 编写任何大的东西以来已经有一段时间了,而且我已经习惯了更现代语言的一些细节。这是一个一直在唠叨我的问题,我相信那里有一个答案。有没有办法在运行时调用用户指定为字符串的函数?无需求助于某种大规模的 switch/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()函数之间的映射。这个map的维护分散到每个写函数的地方,所以没有中心列表或者if语句。

我不会使用像上面那样粗糙的代码来发布任何东西,但我希望你能明白。

于 2012-11-02T20:23:16.660 回答
0

您应该使用 anstd::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>中找到 at,如果你不能在<tr1/unordered_map>. 有关 map 和 unordered_map 的文档可以在以下位置找到:

于 2012-11-02T20:17:55.540 回答