29

我想知道是否有一种简单的方法可以从字符串中调用函数。我知道一种简单的方法,使用“if”和“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;
}

这是基本方法

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

有没有更简单的?

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

4 回答 4

59

您所描述的称为反射,C++ 不支持它。但是,您可能会遇到一些变通方法,例如在这个非常具体的情况下,您可能会使用std::map将函数(std::string对象)的名称映射到函数指针的方法,这在具有相同原型的函数的情况下可能比它可能更容易似乎:

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

请注意,myMap[s](2,3)检索映射到字符串的函数指针s并调用此函数,将其传递23它,使此示例的输出为5

于 2013-10-20T02:44:50.940 回答
25

使用标准字符串到标准函数的映射。

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

使用 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";
}
于 2013-10-20T02:57:30.867 回答
5

您还可以将函数放入共享库中。您将使用 dlopen() 动态加载此类库,然后只需使用 std::string 调用函数。这里有一个例子:

你好.cpp

#include <iostream>

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

主文件

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

汇编:

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

和:

g++ main.cpp -o main -ldl

跑:

C++ dlopen demo

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

这个例子是从这里偷来的。在那里你可以找到关于 dlopen() 和 c++ 的更详细的解释

于 2019-02-13T11:05:13.747 回答
4

还有另一种可能还没有提到,那就是真实的反思。

一个选项是访问从可执行文件或共享库中导出的函数,使用操作系统函数将名称解析为地址。这有一些有趣的用途,例如将两个“参赛者”dll 加载到一个“裁判”程序中,这样人们就可以通过让他们的实际代码相互竞争(玩黑白棋或雷神之锤)来解决它。

另一种选择是访问编译器创建的调试信息。在 Windows 下,这对于兼容的编译器来说非常容易,因为所有工作都可以卸载到系统 dll 或可从 Microsoft 下载的免费 dll。部分功能已包含在 Windows API 中。

然而,这更多地属于系统编程的范畴——不管语言如何——因此它只与 C++ 相关,因为它是卓越的系统编程语言。

于 2014-11-11T13:43:20.800 回答