Question

Ok, so it's been a while since I wrote anything big in c++ and I've grown used to some of the niceties of more modern languages. This is one that's been nagging at me and I'm sure there's an answer out there. Is there any way to call a function specified as a string by a user at run-time? Without having to resort to some sort of massive switch/if block?

The situation I'm in boils down to this: I've got a whole whack-load of math-related problems I've solved in C++ and specified as "Problem1.cpp/Problem1.h", "Problem2.cpp/Problem2.h", etc. Each problem has a function called problemX() (where X is the number of the problem) that kicks off the solution. At the start of the program I'd like to ask the user "Which problem would you like to solve?" and they'd specify a number. I'd then like to call the appropriate problemX() function without having to resort a massive hard coded switch statement (or an if statement, or an indexed array of function pointers, etc).

I'm sure this has got to be possible, but I just can't remember how to go about it. Any ideas?

Was it helpful?

Solution

C++ has no automatic compile or run time reflection of its code in the language. Many library frameworks do have run time reflection of the symbols in a library.

So solution 1: Stick your problems into their own dynamic libraries, and have the main program dynamically load them and look up the symbol names they export.

Solution 2: Replace your raw C-style functions with named objects. So you might have:

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

Above we have some horribly written spew of code that has self-registering Problems (who register in a static map), and a main() that executes whatever problem the string specifies.

A way I think would be cleaner is:

// 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
}

where we do away with the needless class hierarchy and just maintain a map between string and void() functions. The maintenance of this map is distributed to each place where the functions are written, so there is no central list or if statement.

I wouldn't ship anything with code as crude as the above, but I hope you get the idea.

OTHER TIPS

unordered_map of strings to function pointers.

Tweak the user input to ensure it is all lower case (or upper IF YOU LIKE SHOUTING), then just lookup the function. If it exists call it, else error.

You should use an std::map<std::string,_function_pointer_defined_by_you> to store the names of the functions as keys, and the function pointers as values. You could also use an std::unordered_map<std::string,_function_pointer_defined_by_you>, which is something like std::hash_map. If you can use C++11, you will find the std::unordered_map at the header file <unordered_map>, and if you can't at <tr1/unordered_map>. Documentation about both map, and unordered_map can be found at:

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top