How to call a function by its name (std::string) in C++?

63,686

Solution 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

Solution 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";
}

Solution 3

You can also put your functions into a shared library. You will load such library dynamically with dlopen() and then just make the calls to the functions with a std::string. Here an example:

hello.cpp

#include <iostream>

extern "C" void hello() {
    std::cout << "hello" << '\n';
}

main.cpp

#include <iostream>
#include <dlfcn.h>

int main() {
    using std::cout;
    using std::cerr;

    cout << "C++ dlopen demo\n\n";

    // open the library
    cout << "Opening hello.so...\n";
    void* handle = dlopen("./hello.so", RTLD_LAZY);

    if (!handle) {
        cerr << "Cannot open library: " << dlerror() << '\n';
        return 1;
    }

    // load the symbol
    cout << "Loading symbol hello...\n";
    typedef void (*hello_t)();

    // reset errors
    dlerror();

    std::string yourfunc("hello"); // Here is your function

    hello_t hello = (hello_t) dlsym(handle, yourfunc.c_str());
    const char *dlsym_error = dlerror();
    if (dlsym_error) {
        cerr << "Cannot load symbol 'hello': " << dlsym_error <<
            '\n';
        dlclose(handle);
        return 1;
    }

    // use it to do the calculation
    cout << "Calling hello...\n";
    hello();

    // close the library
    cout << "Closing library...\n";
    dlclose(handle);
}

compilation:

g++ -fPIC -shared hello.cpp -o hello.so

and:

g++ main.cpp -o main -ldl

run:

C++ dlopen demo

Opening hello.so...
Loading symbol hello...
Calling hello...
hello
Closing library...

The example was stolen from here. There you can find more detailed explanation on dlopen() and c++

Solution 4

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.

Share:
63,686
Alan Valejo
Author by

Alan Valejo

Updated on February 14, 2020

Comments

  • Alan Valejo
    Alan Valejo about 4 years

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