2

C 側から所有されているポインターがあり、ユーザーデータの形式で Lua スタックにプッシュしたいとします。特定のメタテーブルを指定する機能を犠牲にせずに、これを達成するための最良の方法は何でしょうか?

私の最初のアイデアは、軽いユーザーデータを使用することでしたが、ここで読んだことから、すべての軽いユーザーデータは共通のメタテーブルを共有しています。これは、すべての c-side オブジェクトに共通の動作を強制するため、望ましくありません。

別の this ポインターをメタメソッドに渡すだけで、Lua に割り当てられたオブジェクトのメタテーブルを再利用したいことを考慮して、私の 2 番目のアイデアは、人工的な this ポインターをユーザーデータ オブジェクトにアタッチすることでした。

struct ud
{               
    struct T* ptr;
    struct T data;
};

struct ud* p = (struct ud*)lua_newuserdata(L, sizeof(struct ud));
p->ptr = &(p->data);

または、軽いユーザーデータをプッシュする場合

struct T object;
struct T** p = (struct T**)lua_newuserdata(L, sizeof(struct T*));
*p = &object;

this ポインターを添付することをお勧めしますか、それともより API フレンドリーな代替手段がありますか? 私の最終的な目標は、既存のポインターを Lua にプッシュし、メタテーブルを重いユーザーデータと軽いユーザーデータの両方で再利用できるようにメタテーブルを関連付けることです。

4

1 に答える 1

4

これを行う一般的な(ただし唯一ではない)方法は、「ボックス化されたポインタ」と呼ばれる手法を使用することです。アイデアは、ポインターを保持するのに十分な大きさのユーザーデータを割り当ててから、メタテーブルなどをそれにアタッチすることです。

void push_pointer(lua_State *L, void *p) {
    void **bp = lua_newuserdata(L, sizeof(p));
    *bp = p;

    // setup metatable, etc
}

次に、メタテーブルを介して呼び出された関数がスタック上のこのユーザーデータを受け取り、そこから元のポインターを解凍します。

// called via metatable
int some_function(lua_State *L) {
    assert(lua_type(L, 1) == LUA_TUSERDATA);
    void **bp = lua_touserdata(L, 1);
    void *p = *bp;

    // do stuff with p
}

これを行うときに考慮しなければならないことがいくつかあります。

  • オブジェクトの存続期間。Lua側の何かがそのuserdateへの参照を持っているときに、C側のオブジェクトの割り当てが解除された場合、ダングリングポインターがあります。これが発生する可能性がある場合は、それに対処する方法が必要です。私は、ポインタを使用する前にチェックできる「ライブ」Cオブジェクトのレジストリを用意することで、これに対処しました。

  • ユーザーデータの平等。ポインターごとに新しいユーザーデータをプッシュするため、同じポインターを持つ2つのユーザーデータはLuaで同等に比較されません。それがあなたにとって重要であるならば、あなたがそれに対処することができるいくつかの方法があります。メタ__eqメソッドが最も簡単ですが、常に適切であるとは限りません。上記と同じオブジェクトレジストリを使用してポインタ->userdataマッピングを提供し、プッシュするときに、ポインタのuserdataがすでにある場合は、新しいポインタを作成するのではなく、プッシュするだけです。

于 2013-02-24T01:18:14.253 に答える