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语句。
我不会使用像上面那样粗糙的代码来发布任何东西,但我希望你能明白。