2

C++ オブジェクトのラッパーである lua 'オブジェクト' がいくつかあります。それらは c++ オブジェクトへのローカル参照を保持し、それを呼び出します。

C++ のいくつかの関数がこれらのラッパーを返すようにしたいので、この lua 関数を呼び出してから C++ オブジェクトを設定する必要があります。

クラッシュが発生し、lua スタックを正しく処理していないのではないかと疑っています。たとえば、ラッパー + c++ オブジェクトを作成する関数を終了する前に lua_top を要求すると、結果として 5 が返されます。1 つのオブジェクトを返す場合、1 になるべきではありませんか?

だからここに私がしていることがあります、多分私はそれを間違っています、多分これを行うためのより良い方法があります.

c++、.h:

#define gLuaGet(L, var, type) \
    if (lua_istable(L, 1)) {\
        lua_getfield(L, 1, "CObj");\
        lua_replace(L, 1);\
    }\
    type& var = *(type*)lua_touserdata(L, 1);

#define gLuaCreate(L, type) new (lua_newuserdata(L, sizeof(type))) type();

class MyObject {
    public:
       MyObject();

       int somefunc();
};

int MyObjectCreate(lua_State *L);
int MyObjectCallSomefunc(lua_State *L);

c++、.cpp:

int MyObject::somefunc() {
    std::cerr << "in c++ function" << std::endl;
    return 123;
}

int MyObjectCreate(lua_State *L) {
    gLuaCreate(L, MyObject);
    return 1;
}

int MyObjectCallSomefunc(lua_State *L) {
    gLuaGet(L, obj, MyObject);
    int r = obj.somefunc();
    lua_checkstack(L, 1);
    lua_pushnumber(L, r);
    return 1;
}

ルアラッパー:

function MyObject(donotinit)
    self = {}
    self.CObj = nil

    if (donotinit == nil) then
        self.CObj = MyObjectCreate()
    end

    self.setCObject = function(obj)
        self.CObj = obj
    end

    self.somefunc = function()
        return MyObjectCallSomeFunc(self)
    end

    return self
end

ここで、他のラッパーが C++ 内で作成された MyObject を返すようにしたいので、新しいラッパーから呼び出される C++ コードを次に示します (読みやすくするために、lua_pcall のサニティ チェックを削除しました)。

int returnLuaMyObject(lua_State *L) {
    gLuaGet(L, obj, MyOtherObject);
    MyObject *myObject = obj.getMyObject(); // get c++ part
    lua_getglobal(L, "MyObject"); // create lua part
    lua_pushnumber(L, 1); // and tell it not to initialize the self.CObj
    lua_pcall(L, 1, 1, 0);
    lua_getfield(L, -1, "setCObject"); // call the setCObject function
    lua_pushlightuserdata(L, myObject); // give c++ object as param
    lua_pcall(L, 1, 0, 0);
    // at this point lua_gettop(L); returns 5, can this be correct?
    return 1;
}

さて、lua ラッパーを介してこの関数を数回呼び出すと、すべて問題ないように見えますが、while ループで 50 回呼び出すと、ランダムな時間にクラッシュします (ただし、常に同じ C++ 行で)

私はここで何か間違っていますか?この時点で、lua スタック トップが 5 になっていて、オブジェクトが 1 つしか返されないので問題ありませんか?

4

2 に答える 2

2

これは、私がこれを正しく読んでいる場合、各関数/マクロ呼び出しの後に Lua スタックがどのように見えるかです

int returnLuaMyObject(lua_State *L) {
    // arg
    gLuaGet(L, obj, MyOtherObject); // arg.CObj
    MyObject *myObject = obj.getMyObject();
    lua_getglobal(L, "MyObject"); // arg.CObj _G.MyObject
    lua_pushnumber(L, 1); // arg.CObj _G.MyObject 1
    lua_pcall(L, 1, 1, 0); // arg.CObj obj
    lua_getfield(L, -1, "setCObject"); // arg.CObj obj obj.setCObject
    lua_pushlightuserdata(L, myObject); // arg.CObj obj obj.setCObject myObject
    lua_pcall(L, 1, 0, 0); // arg.CObj obj

    // at this point lua_gettop(L); returns 2.
    // If you end this function with return 1, only obj is returned 
    // to Lua, everything else is discarded. 

    return 1;
}

(念のために言うと、Lua コードを作成するときは、Lua スタックをいじるすべての行にそのようなコメントを宗教的に入れて、何を操作しているかを常に把握できるようにしています。Lua 関数の副作用を覚えると、バグを見つけやすくなります。非常に簡単)

returnLuaMyObject が Lua から呼び出されていると仮定すると、これで問題ありません。C++ のループで呼び出している場合、スタックに 2 つのものが残っており、関数の一部がインデックス 1 で動作するようにハードコードされているため、スタックがめちゃくちゃになります。

これを行うためのより良い方法は、Cubic が提案したことを実行し、マクロではなくいくつかのテンプレートを使用することです。また、関心のあるオブジェクトがスタック上の異なる場所にある状況で関数を再利用できるように、ハードコーディングされたインデックスの使用を可能な限り避ける必要があります。たとえば、gLuaGetどこでも使用できるように、引数としてインデックスを使用する必要があります。(また、引数を取り除きobj、マクロの最後の行全体を削除します。変数objが宣言されている場所が不明確になります。

私は自分用にライブラリを作成しました (偶然にもここにある LuaWrapper と呼ばれます)。luaW_to と luaW_push を使用して、数値や文字列のように Lua からポインターをプッシュおよび取得できます。LuabindやtoLua ++の提案よりも気に入っているので、そこに捨てています。

于 2013-03-17T18:19:15.380 に答える
0

Lua のスタックの一番上は 1 (または ) ではなく -1lua_gettop(state)です。また、これにはマクロではなくテンプレートを実際に使用する必要があります。さらに良いことに、そうしない理由がない限り、luabindまたはtolua++を使用できます。私は現在、基本的に luabind と同じように動作するものを書いていますが、C++11 の機能を使用してブーストの依存関係を削除していますが、まだ完成にはほど遠い状態です。

于 2013-03-17T12:21:59.227 に答える