9

C/C++ を「スクリプト言語」として使用するアーキテクチャを作成しようとしています。私はすでに以下に基づいて動作するプロトタイプを持っています:

http://www.codeproject.com/Articles/29999/Compiling-C-code-at-runtime

http://runtimecompiledcplusplus.blogspot.com

私のプロトタイプでは、ダイナミック リンク ライブラリ / 共有オブジェクトを再コンパイルし、実行時に再ロードできます。次のコードの例を見てみましょう。

[bot.c]

typedef struct
{
    float health;

    float speed;

    bool  alive;

} Bot;

Bot bot = { .health = 100.0f,
        .speed  = 1.0f,
        .alive  = true };

void OnCollision( void )
{
    bot.health -= 10.0f;

    if( bot.health <= 0.0f )
    { bot.alive = false; }
}


void OnUpdate( void )
{
    if( bot.alive )
    {
        /* Update animation */
    }
}

bot.c スクリプトが 1 つのボットに割り当てられている場合は問題ありませんが、コールバックを複数のボットに割り当てると、同じデータを共有します (1 つがヒットすると、すべてがヒットします!)。各ボットに対してボットスクリプトを「個別に」実行するにはどうすればよいですか?

私はすでに C/C++ でコルーチンを研究していますが (setjmp/longjmp を使用)、C++ コンストラクターとデストラクタにはリスクがあります (また、関数内にマクロを統合する必要があります。私の場合、これはユーザーのオプションではありません)。

複数のスレッドを実行することを考えていましたが、同じ問題が発生します (データは dll から取得されるため、共有されるため)。フォーク/コプロセスにはあまり慣れていませんが、私の場合には当てはまらないようです。そして、パイプもオプションだとは思いません...私はかなり行き詰まっています。

とにかくこの問題を解決する方法はありますか?

ps: はい、Lua や Javascript (V8) などのスクリプト言語にはコルーチンが組み込まれていること、または Lua lua_thread の場合は問題を解決できることは知っていますが、ユーザーがコーディングできるように C/C++ インターフェイスを使い続けたいと考えています。ゲームのパフォーマンスは非常に重要です。

4

1 に答える 1

9

コルーチンのようなものは必要ありません。問題は、データを保持する単一のグローバル変数があり、単にその複数のインスタンスが必要なことです。

現在、標準の共有ライブラリ機構では、モジュールの静的データを複製することはできませんが、複製する必要はありません。データを静的に割り当てるのではなく、複数のインスタンスを作成してください。

C++ の方が使いやすいですが、実装するのは多少難しくなります。なぜなら、改ざんされたシンボル名を計算し、new と delete を行うための正しい呪文を計算する必要があるからです。スクリプトは単純に次のようになります。

class Bot {
    float health;
    float speed;
    bool  alive;
    Bot()
    void OnCollision();
    void OnUpdate();
}
Bot::Bot() : health(100.0f), speed(1.0f), alive(true) {}
...

クラス名を知る必要がありますが、それはモジュール名などから派生する可能性があります。初期化をいくらか移植可能に処理するために、一連の静的関数を生成するテンプレートを作成できます。お気に入り:

struct ModuleBase {
    void *(*Init)();
    void (*Done)(void *);
    void (*Collision)(void *);
    void (*Update)(void *);
    ModuleBase(void *(*I)(), void (*D)(void *), void (*C)(void *), void (*U)(void *)) : Init(I), Done(D), Collision(C), Update(U) {}
};
template <typename T>
class Module : public ModuleBase {
    static void *InitFunc() { return static_cast<void *>(new T()); }
    static void DoneFunc(void *x) { delete static_cast<T *>(x); }
    static void CollisionFunc(void *x) { static_cast<T *>(x)->OnCollision(); }
    static void UpdateFunc(void *x) { static_cast<T *>(x)->OnUpdate(); }
public:
    Module() : ModuleBase(&InitFunc, &DoneFunc, &CollisionFunc, &UpdateFunc) {}
};

のように使用

Module<Bot> bot;

「スクリプト」の最後に。そのシンボルを調べて、その中の関数ポインタを呼び出すだけです。

C では、明示的に init 関数と deinit 関数を記述する必要があるため、使用するのがより難しくなりますが、呼び出し方は既に知っています。

struct Bot { ... }
// NO INSTANCES HERE!
void *Init(void) {
    Bot *bot = malloc(sizeof(Bot));
    bot->health = 100.0f;
    bot->speed = 1.0f;
    bot->alive = true;
    return bot;
}
void Done(void *bot) {
    free(bot);
}
void OnCollision(void *void_bot)
{
    Bot *bot = void_bot;
    ...
}
void OnUpdate(void *void_bot)
{
    Bot *bot = void_bot;
    ...
}

どちらの場合でもoperator new[]Botコンストラクターを使用するかInit、適切な引数を使用して関数/メソッドを呼び出して、任意の数のインスタンスを作成するだけです。

于 2012-11-20T14:44:42.760 に答える