2011-01-18 7 views
0

スクリプトエンジンを作成したいと思い、奇妙な問題が見つかりました。 まず、私はそれが(多分)働く方法を説明します: スクリプトエンジン(DLL)は、使用されるアプリケーションに依存しません。メインプログラムのスクリプトエンジン - 不明な関数を呼び出す

// 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のSEは、2つの機能を(このsomelike、それは少し異なる場合があります)をエクスポートします内部DLL関数CallFuncを呼び出し、instrからの入力パラメータを設定します。

この環境でポインタから関数を呼び出してパラメータを設定する方法はありますか?私はこの方法でこれを作成することができますか、または誰かがこれを行う方法、別のアイデアを持っていますか?すべての答えのための

ありがとう:O)

+2

本当に答えはありますが、なぜluaなどを使用しないのですか?これは広く使用されており、独自のプロジェクトが拡張性があり(メタテーブルを使用した構文でさえ)、実装が簡単なものよりも安定しています。 tolua ++のようなC++用のバインディングライブラリがあります。これは、C++関数を自動的にluaでアクセス可能に変換します)。 30分以内に設定することもできます。 – Cray

+0

私はそれについて考えてきましたが、私には好きではないものがあります。しかし、ありがとう、私はこれを私の心に残しておきます:o) –

答えて

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); 

今、私たちは、VMのバイトコードにadd(3.45,8.87);をコンパイルする一つの可能​​性の高い方法を見てみましょう:

pushDouble 8.87 
pushDouble 3.45  
call add 

これは、VMが次の命令を実行する方法です。

  1. データスタックで8.87を押します。
  2. データスタックで3.45を押します。
  3. ローカル関数ディクショナリで "add"という関数を検索します。そこに見つからなければ、プリミティブ関数テーブルを調べます。

VMは、ネイティブ関数テーブルで「追加」見つけて、それを呼び出す:

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

関数が実行を終了するとVMが結果を持つことになります - 12.32 - データスタック上に。

これは非常に簡単な説明でした。実際の実装では、データ・スタックはマシン・ワードのサイズだけの整数を保持することができます。実際にはアドレスにはdoubleの値が含まれます。 popDoubleのような関数は、これらの詳細をネイティブ関数の実装者から隠すべきです。

+0

私はこの権利を理解していれば、メインプログラムの追加機能を変更する必要がありますが、これはできません。私はちょうど書かれた関数のセットを持っています、そして、私はこれらを修正することができないので、私は別の方法でこれを解決しなければなりません。 1つはSEレベルでの単純な操作であり、もう1つは「システム機能」です。 printfまたはfopen ....私の1つの目標は、スクリプトエンジン用の関数のラッパーを書くことです。o) –

+0

@Ellesmess Glain:これは本当にうまくいかないでしょう。あなたの 'SetFunc'は現在その関数型に' void * func'を使います。これは間違っています。ポインタを使って関数を呼び出すには、正確な型が必要です。テンプレートを使うと、やや簡単にできますが、 'printf(const char *、...)'のような関数を総称的に呼び出すことはできません。 – MSalters

関連する問題