web-dev-qa-db-fra.com

Comment appeler une fonction par son nom (std :: string) en C ++?

Je me demande s'il existe un moyen simple d'appeler une fonction à partir d'une chaîne. Je connais un moyen simple, en utilisant "si" et "sinon".

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

Ceci est l'approche de base

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

Y a-t-il quelque chose de plus simple?

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

Ce que vous avez décrit s'appelle réflexion et C++ ne le prend pas en charge. Cependant, vous pouvez venir avec une solution de contournement, par exemple dans ce cas très concret, vous pouvez utiliser un std::map Qui mapperait les noms des fonctions (std::string Objets) aux pointeurs de fonction, qui en cas de les fonctions avec le même prototype pourraient être plus faciles qu'il n'y paraît:

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

Notez que myMap[s](2,3) récupère le pointeur de fonction mappé sur la chaîne s et appelle cette fonction, en lui passant 2 Et 3, Ce qui rend la sortie de cet exemple être 5

44
LihO

Utilisation d'une carte de chaîne standard vers des fonctions standard.

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

Encore mieux avec 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";
}
21
Martin York

Il existe une autre possibilité qui n'a pas encore été mentionnée, qui est la réflexion true.

Une option pour cela consiste à accéder aux fonctions exportées à partir d'un exécutable ou d'une bibliothèque partagée à l'aide des fonctions du système d'exploitation pour résoudre les noms en adresses. Cela a des utilisations intéressantes comme le chargement de deux dll `` compétiteurs '' dans un programme `` arbitre '', afin que les gens puissent s'en débarrasser en faisant combattre leurs codes réels (en jouant Reversi ou Quake, peu importe).

Une autre option consiste à accéder aux informations de débogage créées par le compilateur. Sous Windows, cela peut être étonnamment facile pour les compilateurs compatibles, car tout le travail peut être déchargé sur des DLL système ou des DLL gratuites téléchargeables auprès de Microsoft. Une partie de la fonctionnalité est déjà contenue dans l'API Windows.

Cependant, cela tombe davantage dans la catégorie de la programmation système - quel que soit le langage - et donc il ne concerne le C++ que dans la mesure où il est le langage de programmation système par excellence.

4
DarthGizka

Vous pouvez également placer vos fonctions dans une bibliothèque partagée. Vous allez charger une telle bibliothèque dynamiquement avec dlopen () et ensuite juste faire les appels aux fonctions avec une chaîne std ::. Voici un exemple:

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

et:

g++ main.cpp -o main -ldl

courir:

C++ dlopen demo

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

L'exemple a été volé à ici . vous pouvez trouver des explications plus détaillées sur dlopen () et c ++

4
jav