脚本引擎 – 调用“未知”function

我想创建脚本引擎,我发现了奇怪的问题。 首先我解释一下它将如何工作:脚本引擎(DLL)将独立于将要使用的应用程序。 SE将导出两个函数(有些类似,它可能有点不同):

// This will be called on beginning of host program, register set of functions, that // script will provide to usere SetFunc(void *func,byte code,char *descript); func : pointer to existing function from host application (eg printf) code : code for script compiler descript : func description, if eventually needed // function calling functions inc Script Engine (in compiled DLL, which can't be // modified for host application, only input for SE is SetFunc CallFunc(void *instr); instr : pointer to memory block, in which is stored (instr_code - byte)(void* - pointer to func)(params_len - unsigned int)(params - data block) /\--- this will be filled after loading script to SE, according to table of registred functions (via Setfunc). Callfunc(void *func,void *params,unsigned int param_length); func : pointer to function params : parameters for function in memory block (extracted from instr) param_length : what to say :o) 

主程序示例:

 #include "ScriptEngine.h" // this will create connection to SE DLL float add(double num1,double num2) { return (num1+num2); } int main() { SetFunc(add,1,"f:d,d/2"); // register one function to SE LoadScript("simple.sc","simple"); // load script to memory ExecuteScript("simple"); // execute script (only add two nums) } 

和脚本:

 main() { add(3.45,8.87); } // after some compilation to binary format : ... (1)(NULL)(16)(3.45)(8.87) (instruction for "system call" (registred via SetFunc) ... // after LoadScript (1)(0x00402cc)(16)(3.45)(8.87) 

并且在ExecuteScript上调用内部DLL函数CallFunc,并从instr输入设置参数。

如何从指针调用函数并在此环境中设置参数? 我可以通过这种方式创建这个,或者有人有另外的想法,如何做到这一点?

感谢所有答案:o)

一种不同的,更简单的方法:将脚本编译为基于堆栈的虚拟机的指令。 应该要求所有本机函数遵循统一签名,以便我们可以使用单个函数指针typedef来调用它们。 例如,以下是我们如何实现并向脚本引擎公开add函数:

 // Common type for all native functions. typedef void(*NativeFuncPtr)(VM*); // The Virtual Machine is wrapped in a `VM` object. // popDouble() and pushDouble() make use of the more primitive // stack operations push() and pop(). void add(Vm* vm) { double a = vm->popDouble(); double b = vm->popDouble(); vm->pushDouble(a + b); // leave the result on the VM stack. } // The call to setFunc will map the string "add" to the address of the // `add()` function in a hashtable. vm->setFunc("add", &add); 

现在让我们看一下编译add(3.45,8.87);可能方法add(3.45,8.87); 到VM字节码:

 pushDouble 8.87 pushDouble 3.45 call add 

这是VM执行这些指令的方式:

  1. 在数据堆栈上按8.87。
  2. 在数据堆栈上按3.45。
  3. 在本地函数字典中搜索名为“add”的函数。 如果在那里找不到它,则在原始函数表中查找。

VM在本机函数表中找到“add”并调用它:

 NativeFuncPtr fptr = nativeFunctions["add"]; (*fptr)(this); 

一旦函数完成执行,VM将在数据堆栈上得到结果 – 12.32

这是一个非常简单的解释。 在实际实现中,数据栈可能只能保存机器字大小的整数。 所以它实际上将包含double值的地址。 像popDouble这样的popDouble应该隐藏本机函数实现者的这些细节。