2016-12-31 5 views
1

後で非同期で呼び出すためにLuaクロージャを格納する方法が必要です。lua - Cでストアを閉じ、Cでasyncを呼び出す

  1. 第二のアイデアは、私はそれをプッシュし、後でそれを呼び出すことができ、メタテーブルにクロージャを保存することでしたが、
  2. 直接私の最初のアイデアだったlua_tocfunctionが、閉鎖がcfunctionではなく、Cから呼び出すことができませんそれは私がコピー閉鎖できないようです。 ()。

だから私はあなたの助けが必要です。クロージャーはどのように保存できますか?

私はどこかからその部分をコピーしたので、なぜ私のルアー・コーラーに__indexフィールドがあるのか​​完全に理解していないことを認めます。

ところで:onrenderのないプログラムは期待どおりに動作しました。私はqt guiを使用していて、の後には qtのメインループの後にルア状態が閉じられているので、作成されたウィンドウはスクリプトの後に__gcによって削除されません。

bootstrap.lua

local w = w_render() -- create window object 
w:show() 

w:onrender(function() 
    print('render') 
end) 

w_lua.cppあなたの問題は、間違ったインデックスを使用してから派生し、間違った上でフィールドを設定/取得しようとしているように見えます

// chlua_* are helper macros/templates/methods 
// 1: self 
// 2: render closure 

int w_render_onrender(lua_State *L) { 
    auto *self = chlua_this<GLWindow *>(L, 1, w_render_table); 

    lua_pushvalue(L, 2); // copy closure to top 
    lua_setfield(L, 2, "onrender_cb"); // save closure in metatable 
    // !!! ERROR: attempt to index a function value 



    self->onrender([L](){ 
     lua_getfield(L, 2, "onrender_cb"); 
     qDebug() << "onrender"; 
     lua_call(L, 0, 0); 
    }); 

    return 0; 
} 

// Creates the object 
int w_render(lua_State *L) { 
    auto *&self = chlua_newuserdata<GLWindow *>(L); 
    self = new GLWindow; 

    if (luaL_newmetatable(L, w_render_table)) { 
     luaL_setfuncs(L, w_render_methods, 0); 
     lua_pushvalue(L, -1); 
     lua_setfield(L, -2, "__index"); 
    } 

    lua_setmetatable(L, -2); 
    return 1; 
} 
+0

あなたはインデックスが右 'w_render_onrender'であるよろしいです:私は仮定していますが、self->onrenderが実際に実行したときにスタック上にあると何が起こるかを気にする必要はありません。この方法では、非同期のですか?私はあなたの 'GLWindow * 'を表す自己udataが最初のものであると仮定しています。この場合、 'lua_setfield(L、-3、" onrender_cb ");や' lua_setfield(L、1、 "onrender_cb"); ' – greatwolf

+0

私はあなたがする必要があると思います 'lua_getmetatable(L、1);実際にフィールドを設定しようとする前に、lua_insert(L、-2);私はpushvalueが本当にここで本当に必要であるか分からない。あなたが 'closure 'を取り戻す' lua_getfield'を行っているので、あなたの 'onrender'ラムダではこれは使われません。 – greatwolf

+0

@greatwolf私はあなたの2つのソリューションを試しました:-3または1は私に 'ユーザデータ値をインデックス化しようとしました 'を与え、2番目の解決策は'私はユーザデータ値をインデックス化しようとします。私が気づいたことの1つは、get/setfieldは[doc](https://www.lua.org/manual/5.2/manual.html#lua_setfield)から '1'で呼び出されるべきです、ということです。インデックスはテーブルがスタックに格納されている場所で、ラムダの実装がどのように確実で、テーブルが存在し、その間にスタックが変更されたのか不思議です。 – Aitch

答えて

1

スタック上のluaオブジェクト。このようなコードを変更してみてください、あなたのGLWindow *を表すUDATAが第一、第二Luaの閉鎖が続いていると仮定:

int w_render_onrender(lua_State *L) 
{ 
    luaL_checkudata(L, 1, w_render_table); 
    luaL_checktype(L, 2, LUA_TFUNCTION); 
    auto *self = chlua_this<GLWindow *>(L, 1, w_render_table); 

    lua_getmetatable(L, 1); 
    lua_insert(L, -2);  // GLWindow GLWindow_mt lua_closure 
    lua_setfield(L, -2, "onrender_cb"); // save closure in metatable 


    self->onrender([L]() 
    { 
    luaL_checkudata(L, 1, w_render_table); 
    // assuming GLWindow udata is self and onrender_cb is your lua closure above 
    // access GLWindow.onrender_cb through GLWindows's metatable 
    lua_getfield(L, 1, "onrender_cb"); 
    qDebug() << "onrender"; 
    luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure 
    lua_call(L, 0, 0); 
    }); 

    return 0; 
} 

編集:はいくつかのより多くの本について考えた後、それはおそらくLuaの参照を作成するために、より理にかなっていますluaL_refを使用してください。

int w_render_onrender(lua_State *L) 
{ 
    luaL_checkudata(L, 1, w_render_table); 
    luaL_checktype(L, 2, LUA_TFUNCTION); 
    auto *self = chlua_this<GLWindow *>(L, 1, w_render_table); 

    auto lua_cb = luaL_ref(L, LUA_REGISTRYINDEX); 
    // just to check that what's on the stack shouldn't matter 
    lua_settop(L, 0); 

    self->onrender([L, lua_cb]() 
    { 
    lua_rawgeti(L, LUA_REGISTRYINDEX, lua_cb); 
    luaL_checktype(L, -1, LUA_TFUNCTION); // Just to be sure 
    qDebug() << "onrender"; 
    lua_call(L, 0, 0); 
    luaL_unref(L, LUA_REGISTRYINDEX, lua_cb); // assuming you're done with it 
    }); 

    return 0; 
} 
+0

2番目のコードスニペットは魅力的な作品です。ありがとうございました!すべての悪の根源は、未知のスタック 'L'のため、非同期のCコードにメタテーブルへのアクセスに問題があるということですか? lua_CFunctionでそれをフェッチして、それをlambdaに渡すことができるようにするためには、(lua ctorの)udata自身を一度参照する方がよいでしょうか?この方法では、オブジェクトごとに多くのコールバックがあると仮定しているため、オブジェクトごとに1つの参照しかありません。それにもかかわらず、私はあなたの答えを受け入れられたとマークします:)。 – Aitch

+0

@Aitchはいあなたはudataにlua_cbを割り当ててから、udataを参照して(ちょうど '__newindex'を提供するか、udataインデックスエラーをもう一度受け取ります)。簡単にするためにコールバックを直接引用しました。 – greatwolf

+0

@Aitch 'self-> onrender'が本当にasyncの場合、コールバック自体が実際に起こる前に、いくつかのことがlua vm上で実行される可能性があります。その場合、実際にスタック上にあったものは、 'w_render_onrender'が呼び出された時とは大きく異なることがあります。非同期呼び出しがうまく動作するのは少し驚きですが、luaのデザインには内部ロックや同期プリミティブがありません。 'L'が一貫性があり、バイトコード命令の実行中にprempt'edにならなかったことをどうやって確認しますか? – greatwolf

関連する問題