How to call a function by its name (std::string) i

2019-01-10 13:34发布

问题:

I wonder if there is a simple way to call a function from a string. I know a simple way, using 'if' and 'else'.

int function_1(int i, int j) {
    return i*j;
}

int function_2(int i, int j) {
    return i/j;
}

...
...
...

int function_N(int i, int j) {
    return i+j;
}

int main(int argc, char* argv[]) {
    int i = 4, j = 2;
    string function = "function_2";
    cout << callFunction(i, j, function) << endl;
    return 0;
}

This is the basic approach

int callFunction(int i, int j, string function) {
    if(function == "function_1") {
        return function_1(i, j);
    } else if(function == "function_2") {
        return function_2(i, j);
    } else if(...) {

    } ...
    ...
    ...
    ...
    return  function_1(i, j);
}

Is there something simpler?

/* New Approach */
int callFunction(int i, int j, string function) {
    /* I need something simple */
    return function(i, j);
}

回答1:

What you have described is called reflection and C++ doesn't support it. However you might come with some work-around, for example in this very concrete case you might use an std::map that would map names of functions (std::string objects) to function pointers, which in case of functions with the very same prototype could be easier than it might seem:

#include <iostream>
#include <map>

int add(int i, int j) { return i+j; }
int sub(int i, int j) { return i-j; }

typedef int (*FnPtr)(int, int);

int main() {
    // initialization:
    std::map<std::string, FnPtr> myMap;
    myMap["add"] = add;
    myMap["sub"] = sub;

    // usage:
    std::string s("add");
    int res = myMap[s](2,3);
    std::cout << res;
}

Note that myMap[s](2,3) retrieves the function pointer mapped to string s and invokes this function, passing 2 and 3 to it, making the output of this example to be 5



回答2:

Using a map of standard string to standard functions.

#include <functional>
#include <map>
#include <string>
#include <iostream>

int add(int x, int y) {return x+y;}
int sub(int x, int y) {return x-y;}

int main()
{
    std::map<std::string, std::function<int(int,int)>>  funcMap =
         {{ "add", add},
          { "sub", sub}
         };

    std::cout << funcMap["add"](2,3) << "\n";
    std::cout << funcMap["sub"](5,2) << "\n";
}

Even better with Lambda:

#include <functional>
#include <map>
#include <string>
#include <iostream>

int main()
{
    std::map<std::string, std::function<int(int,int)>>  funcMap =
         {{ "add", [](int x, int y){return x+y;}},
          { "sub", [](int x, int y){return x-y;}}
         };

    std::cout << funcMap["add"](2,3) << "\n";
    std::cout << funcMap["sub"](5,2) << "\n";
}


回答3:

There is another possibility which hasn't been mentioned yet, which is true reflection.

An option for this is accessing functions exported from an executable or a shared library using operating system functions for resolving names to addresses. This has interesting uses like loading two 'contestant' dlls into an 'umpire' program, so that people can slug it out by having their actual codes fight each other (playing Reversi or Quake, whatever).

Another option is accessing the debug information created by the compiler. Under Windows this can be surprisingly easy for compilers that are compatible, since all the work can be off-loaded to system dlls or free dlls downloadable from Microsoft. Part of the functionality is already contained in the Windows API.

However, that falls more into the category of Systems Programming - regardless of language - and thus it pertains to C++ only insofar as it is the Systems Programming language par excellence.