D语言中的可变函数调用

5

好的,这是我需要的:

  • 假设我们有一个字符串变量,比如func,它的值由用户给出(不是常量)
  • 我们如何调用一个函数,其名称为func的值?

更新:

也许认为我需要的很清楚明白,但由于我注意到有很多反对票,所以在这里解释一下:

string func = "myfunc";

我需要类似于call(func)的东西,它将调用myfunc。 就像PHP中的call_user_func_array()
有什么想法吗?
我看了一下混合(mixins),但这看起来不是我需要的。
P.S. 我也使用和标记了问题,因为通用解决方案在这些语言之间可能是理论上可能的,因为它们之间有相似之处。

3
我可以理解你的问题是“如何带参数调用函数”。对应的语法是functionName(func)。但我猜想这可能不是你真正想问的问题,请进一步澄清你的问题。 - Vladimir Panteleev
2
@CyberShadow - 我理解为“用户输入一个值到一个字符串中,该字符串是一个函数名,我如何调用该函数”。 - Mike
3
创建一个名字到函数指针的哈希表,查找表中对应的字符串并调用相应的结果。 - user3286380
1
哦...那样的话,祝你好运!特别是在C++中,因为函数名被编码了。 - Cory Kramer
1
请查看此答案https://dev59.com/qHNA5IYBdhLWcg3wBpHs#1118808,但我猜它只适用于`C`,而且仅适用于在您的可执行文件中定义的函数。 - Dabo
显示剩余8条评论
6个回答

4

通常编程语言在运行时无法访问函数名称,因为链接器通常会将这些名称剥离,只留下机器码。

正如用户3286380所建议的那样,解决此问题的一种方法(适用于所有编程语言)是创建一个表格,将函数名称和指向相应函数的指针进行交叉引用。然后,在运行时,您的程序可以查找此表格。

如果所使用的语言允许内省(运行时或编译时),则可以枚举模块或程序中的函数,并查找用户输入的函数名称。在D语言中,您可以使用allMembers trait来获取模块中的所有声明数组;下一步是过滤函数,检查函数签名是否匹配,然后生成一个调用相应函数的switch语句。您可以使用CTFE和字符串mixin来实现上述操作。


没错,这就是我期望的答案!非常感谢!(我正在研究它,看起来它确实是我需要的;想象一下,我以为我已经浏览了大部分文档!...) - Dr.Kameleon
1
@Dr.Kameleon 如果您能将您的函数放在一个类中,您就可以使用 Object.factory 来创建一个实例并在其上调用该函数(适用于 D 语言)。 - user3286380
@user3286380 嗯...我想我明白你的意思了。相当有趣的方法!;-) - Dr.Kameleon

1
我以前做过这个的唯一方法是让你的可执行文件中的函数与一个“命名”该函数的字符串链接,然后进行比较。
我想为您提供一个简单的(1个函数)C示例,我认为这在D中也适用:
typedef struct {
    char * func_name;
    void (* fptr)();
} myS;

void print(){
    printf("hello world\n");
}

int main()
{ 
    myS Ex;
    char str[100] = "";

    // Obviously you can fill these in a more intelligent manner, use an array, etc
    Ex.func_name = malloc(strlen("print")+1);
    strcpy(Ex.func_name, "print");
    Ex.fptr = print;

    printf("Enter the function name\n");
    scanf("%99s", str);

    // and of course with a number of functions to examine you could use a switch
    if(!strcmp(str, Ex.func_name))
        Ex.fptr();
    else
        printf("Didn't find a function to call\n");

    return 0;
}

1
创建一个二元组数组,其中每个二元组的第一个元素是字符串函数名,第二个元素是指向函数的指针。搜索该数组,当找到名称时,使用指针调用函数。请注意,Python具有字典类,可以将其作为键值对来使用。

对于这种数据结构,哈希表(C++中的std::unordered_map,D语言内置,类似于Python的dict)或二叉树(C++中的std::map)比成对数组更高效。 - Antoine

1

我看到有三种方法可以完成这个任务:

  • 如果你事先知道一系列可供选择的函数,你可以注册一个关联表格,其中键为string,值为functor,其中functor记录函数地址和签名,类似于C++中的std::function

  • 否则,更复杂的解决方案是检查自己可执行文件的调试符号,并从中构建表格。您还可以使用预处理器(如Qt的MOC)来构建此表格,或者在D语言中使用内置的编译时反射。

  • 如果您真的需要灵活性,请使用解释器而不是编译器。Cling是一个基于LLVM的不错的C++解释器,具有即时编译功能。


0
我使用关联数组的方式。一个例子是:
module main;

import std.stdio;

class Tqfuncs{
private:
    void qwrite(string arg){
        write(arg);
    }
    void delegate(string)[string] pList;//store name & pointer to functions here
public:
    this(){
        pList = [
            "qwrite":&qwrite,//add the qwrite function
        ];
    }
    void call(string name, string arg){
        if (name in pList){
            pList[name](arg);//call the function
        }else{
            throw new Exception("unrecognized function call "~name);
        }
    }
}

void main(string[] args){
    Tqfuncs scrF = new Tqfuncs;
    scrF.call("qwrite","This will be written");
}

我使用这种方法创建了一种动态脚本语言,您可以使用源代码https://github.com/Nafees10/qscript了解更多信息。
但是,使用此方法存在一些限制:

  • 所有函数必须符合特定的“格式”。就像我使用了void functionName(string arg)。但是有一个解决方法,如果您感兴趣,可以在QScript(我的项目)中查看它。

0
不需要在静态类型语言中这样做!毫无疑问。
为什么?因为编译器无法预知运行时变量的值(已更改)来保存您想要运行的函数名称,所以当它遇到funcname(myvar)时,应该生成什么机器代码?
想象一下你想要的东西:
string funcname;
// here some initialisation code that modifies the funcname
int someVal = funcall(funcname);

当funcall()得到一个返回类型为Person(用户自定义类型)的函数名称时,会发生什么?

此外,当没有名为 funcname 的函数时会发生什么?运行时错误?没有错误?什么?

你想要的是动态语言领域,在这个领域中类型安全完全被忽略(不幸的是大多数语言都是如此)。

然而...如果你的所有函数都包含在一个结构体中,那么D语言提供了一种优雅的方法来实现你想要的功能,即通过转发(函数分派)。在这里阅读更多信息:https://dlang.org/spec/operatoroverloading.html#dispatch


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接