我想创建脚本引擎,我发现了一个奇怪的问题。 首先我解释一下它将如何工作: 脚本引擎(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 (e.g. 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)
答案 0 :(得分:0)
一种不同的,更简单的方法:将脚本编译为基于堆栈的虚拟机的指令。应该要求所有本机函数遵循统一签名,以便我们可以使用单个函数指针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);
编译为VM字节码的可能方法:
pushDouble 8.87
pushDouble 3.45
call add
这是VM执行这些指令的方式:
VM在本机函数表中找到“add”并调用它:
NativeFuncPtr fptr = nativeFunctions["add"];
(*fptr)(this);
一旦函数完成执行,VM将在数据堆栈上得到结果 - 12.32
。
这是一个非常简单的解释。在实际实现中,数据堆栈可能只能保存机器字大小的整数。因此它实际上将包含double
值的地址。像popDouble
这样的函数应该隐藏本机函数实现者的这些细节。