Script engine - calling "unknown" funcs
I want to create script engine and I found strange problem. First I explain, how it will (maybe) work : Script engine (DLL) will be independent on application in which will be used. SE will export two functions (somelike this, it can be a little bit different) :
// 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 fu开发者_如何学Pythonnctions 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)
Example of main program :
#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)
}
And Script :
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)
And on ExecuteScript call internal DLL function CallFunc and on it's input set parameters from instr.
How I can call function from pointer and set parameters in this environment? Am I able to create this by this way, or somebody has another ideas, how to do this?
Thanks for all answers :o)
A different, simpler approach: compile the script to the instructions of a stack-based virtual machine. All native functions should be required to follow a uniform signature so that we can use a single function pointer typedef
to call them. For example, here is how we could implement and expose the add
function to the script engine:
// 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);
Now let us look at one probable way to compile add(3.45,8.87);
to VM byte-codes:
pushDouble 8.87
pushDouble 3.45
call add
This is how the VM will execute these instructions:
- Push 8.87 on the data stack.
- Push 3.45 on the data stack.
- Searches for a function called "add" in the local function dictionary. If it is not found there, looks in the primitive functions table.
The VM finds "add" in the native functions table and invokes it:
NativeFuncPtr fptr = nativeFunctions["add"];
(*fptr)(this);
Once the function finishes execution the VM will have the result - 12.32
- on the data stack.
That was a very simple explanation. In actual implementation the data stack might be able to hold only integers the size of a machine word. So it will actually contain the address of double
values. Functions like popDouble
should hide these details from the native function implementor.
精彩评论