使用C ++中的函数名称从字符串调用函数

时间:2009-11-28 03:34:20

标签: c# c++

如何从字符串中调用C ++函数?

而不是直接从字符串调用方法:

void callfunction(const char* callthis, int []params)
{
  if (callthis == "callA")
  {
    callA();
  }
  else if (callthis == "callB")
  {
    callB(params[0], params[1]);
  }
  else if (callthis == "callC")
  {
    callC(params[0]);
  }
}

在C#中我们使用typeof()然后从那里获取方法信息和调用...我们可以在C ++中使用什么?

8 个答案:

答案 0 :(得分:10)

创建一个由字符串和函数指针组成的std :: map。使用您要调用的所有函数创建地图。

还有其他方法可以做到这一点,涉及符号表和动态加载器,但这些方式不可移植或友好。

答案 1 :(得分:4)

其他解决方案是同一主题的变体:

switch (callthis)
{
case FUNCA:  callA();    break;
case FUNCB:  callB(params);  break;
... etc.
}

或搜索一系列结构:

struct {
    char *name;
    TFunc  f;
} funcdefs [] = {
    {"callA", callA},
    {"callB", callB},
    {"callC", callC},
    ... etc.
    {NULL, NULL}
};
for (int j = 0;  funcdefs [j] .name;  ++j)
    if (!strcmp (funcdefs [j] .name, callthis))
    {
         funcdefs [j] .f (params);
         break;
    }

答案 2 :(得分:4)

您还可以查看How can I add reflection to a C++ application?的答案,了解有关RTTI的信息,这是C ++中的反射机制。

祝你好运。

答案 3 :(得分:2)

替代方法:您可以使用函数指针数组并使用其索引调用所需的函数。

typedef void (*TFunc)(int, int);

TFunc arrptr[100];

void callFunction(int index, int params[])
{
    (*arrptr[index])(params[0], params[1]);
}

答案 4 :(得分:2)

可能不是一个选项,但是如果你可以使用托管c ++(C ++ / CLI),你就可以像在C#中那样做。这将需要.NET虽然......

答案 5 :(得分:2)

没有好办法自动完成你的要求。我会考虑两种不同的方式,具体取决于您认为需要调用多少函数:

如果只有少数函数,请坚持使用您的代码(请注意,如果您希望使用const char *,则无法将这些字符串与==运算符进行比较。您可以使用“strcmp()” ,通过做一个“#include< cstring>”)。

如果有许多功能,或者您经常在列表中添加和删除功能,那么您可能需要使用“std :: map”。这会将函数名称字符串映射到函数指针。我可能会把它包装在一个易于使用的类中:

class Str2Fun {
  std::map<std::string, (void*)(int**)> data;
 public:
  void add( const char *funName, (void*)(int**) funPtr );
  void call( const char *funName );
};

答案 6 :(得分:1)

一般不可能性

在纯标准C++11中,如果要调用程序的任意可能函数(或者调用程序的任意可能函数),就不能真正实现该目标(从名称调用函数,在运行时通过某些任意字符串给出)。它所使用的库)来自函数名称。您确实可以按照Lua的建议嵌入口译员(例如GNU guilextofl's answer)。

保持名称与功能之间的关联

如果您知道一组可能可调用的函数,并且它们共享一个共同的签名(例如typedef int signature_T(int[])),您可能只使用从名称到函数的关联,例如某些std::map<std::string,std::function<signature_T>>; BTW, C ++ 11的新功能(闭包,std::functionauto,lambdas)应该会有很多帮助。

在POSIX

上使用动态链接工具,例如dlsym

如果您将自己局限于像Linux这样的POSIX系统,您可以考虑其他一些技巧:使用dlsym(3)dlopen(3)从某些unmangled name获取函数指针。因此,您要通过名称extern "C"声明要呼叫的功能(要禁用name mangling),您可以将程序与-rdynamic-ldl库相关联。您dynamically load plugins dlopendlopen获取NULL - dlsym - 并使用其名称获取函数指针dlsym。阅读C++ dlopen mini howto和Drepper的How To Write Shared Libraries论文。

(Windows也具有与libffi几乎相同的功能,因为它还有dynamic linker;但我从未使用过Windows

一些C ++框架库(QtPOCO)以独立于操作系统的方式包装动态加载插件。

使用dlopen

调用任意函数

关于调用任意签名的功能存在问题。在C语言或C ++中调用(或来自)任何函数时,您绝对需要知道签名(至少在运行时,通常在编译时)(因为ABI指示了各种calling conventions)。您可以使用libffi

  

某些程序在编译时可能不知道要将哪些参数传递给函数。例如,可以在运行时告知解释器关于用于调用给定函数的参数的数量和类型。 Libffi可用于此类程序,以提供从解释程序到编译代码的桥梁。

即时生成机器代码

最后,您可以使用一些JIT library在运行时生成一些机器代码:GNU lightninglibjitasmjit之类的简单JIT库能够快速生成一些慢机器代码,或基于复杂编译器的JIT框架,如libgccjitLLVM,它们生成优化的快速机器代码(但需要大量的生成时间,如编译器)。

在运行时生成C(或C ++代码),编译它并/tmp/foobar.c - 它

一个相关的技巧只是在一个临时文件gcc -fPIC -O -shared /tmp/foobar.c -o /tmp/foobar.so中生成一些C或C ++代码,用dlopen将它编译成一个插件(所以要求position independent code),然后使用dlopen动态加载该临时插件。我在MELT中使用了这样一个技巧(一种Lispy域特定语言来扩展和定制GCC,可用的免费软件GPLv3)。在实践中,编译器今天足够快,甚至允许交互式地使用这样的用户:用户可以在某些DSL中键入一个简单的表达式,基础结构正在将该DSL转换为C代码,然后将该代码编译成一个插件,该插件稍后{{ 1}} - 编辑。这在实践中对于交互式使用来说足够快(因为生成的C代码每个交互式表达式将有几百行,并且编译只需要几分之一秒。)

其他编程语言

某些编程语言为homoiconic和/或具有某些eval功能,或启用multi-stage programming。有些实现甚至能够在运行时生成非常高效的机器代码,特别是SBCL始终在每个表达式中转换为机器代码,即使在read-eval-print loop中也是如此。

答案 7 :(得分:0)

您可以将函数声明包装为解释语言(如Lua或Perl或Python (boost has some nice framework for that))。然后使用该语言“按字符串”调用代码。

这些语言/包装器是为了做这些事情而构建的。 C ++不是,所以你会付出很多努力来增加对它的支持。