代码之家  ›  专栏  ›  技术社区  ›  Zack Lee

在C++中如何将用户数据从一个LUA块传递到另一个LUA块

  •  1
  • Zack Lee  · 技术社区  · 6 年前

    我在试着 userdata chunk A 在C++中(通过我例子中函数返回的变量),然后,通过这个 用户数据 回到Lua脚本( chunk B 用户数据 块A .

    MyBindings.h

    class Vec2
    {
    public:
        Vec2():x(0), y(0){};
        Vec2(float x, float y):x(x), y(y){};
        float x, y;
    };
    

    MyBindings.i

    %module my
    %{
        #include "MyBindings.h"
    %}
    
    %include "MyBindings.h"
    

    main.cpp

    #include <iostream>
    #include <lua.hpp>
    
    extern "C"
    {
        int luaopen_my(lua_State *L);
    }
    
    int main()
    {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        luaopen_my(L);
        lua_settop(L, 0);
        /* chunk A */
        luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
                         "function setup()\n"
                           "return vec2\n"
                         "end\n");
        /* chunk B */
        luaL_dostring(L, "function test(p)\n"
                           "print(p.x)\n"
                         "end\n");
        void *userDataPtr = nullptr;
    
        /* call setup function */
        int top = lua_gettop(L);
        lua_getglobal(L, "setup");
        if (lua_pcall(L, 0, LUA_MULTRET, 0))
        {
            std::cout << lua_tostring(L, -1) << '\n';
            lua_pop(L, 1);
        }
        /* check the return value */
        if (lua_gettop(L) - top)
        {
            /* store userdata to a pointer */
            if (lua_isuserdata(L, -1))
                userDataPtr = lua_touserdata(L, -1);
        }
        /* check if userDataPtr is valid */
        if (userDataPtr != nullptr)
        {
            /* call test function */
            lua_getglobal(L, "test");
            lua_pushlightuserdata(L, userDataPtr); /* pass userdata as an argument */
            if (lua_pcall(L, 1, 0, 0))
            {
                std::cout << lua_tostring(L, -1) << '\n';
                lua_pop(L, 1);
            }
        }
        lua_close(L);
    }
    

    我得到的结果是:

    [string“本地向量2=我的.Vec2(3,4)…“]:6:尝试索引

    我期望的结果是:

    用户数据 块A 然后把这个传给 块B ?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Henri Menke    6 年前

    除了另一个答案之外,我还想展示一个变量,您可以在其中存储对Lua注册表中的值的引用。这种方法的优点是,您不必将值保留在堆栈上并考虑偏移量是多少。另请参见 27.3.2 – References 在Lua中编程。

    这种方法使用三种功能:

    1. int luaL_ref (lua_State *L, int t);

      t 并返回该值在该表中的索引。因此,为了在注册表中保存一个值,我们使用

      userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
      
    2. int lua_rawgeti (lua_State *L, int index, lua_Integer n);

      将元素的值推送到堆栈上 n index t[n] 在卢阿)。因此要在索引处检索值 userDataRef 从我们使用的注册表

      lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
      
    3. void luaL_unref (lua_State *L, int t, int ref);

      删除索引中存储的引用 ref 在桌子上 t型 裁判 可重复使用。因此要删除引用

      luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
      
    #include <iostream>
    #include <lua.hpp>
    
    extern "C" {
    int luaopen_my(lua_State *L);
    }
    
    int main() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        luaopen_my(L);
        lua_settop(L, 0);
        /* chunk A */
        luaL_dostring(L, "local vec2 = my.Vec2(3, 4)\n"
                         "function setup()\n"
                           "return vec2\n"
                         "end\n");
        /* chunk B */
        luaL_dostring(L, "function test(p)\n"
                           "print(p.x)\n"
                         "end\n");
        int userDataRef = LUA_NOREF;
    
        /* call setup function */
        int top = lua_gettop(L);
        lua_getglobal(L, "setup");
        if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
            std::cout << lua_tostring(L, -1) << '\n';
            lua_pop(L, 1);
        }
        /* check the return value */
        if (lua_gettop(L) - top) {
            /* store userdata to a pointer */
            userDataRef = luaL_ref(L, LUA_REGISTRYINDEX);
        }
    
        /* check if userDataRef is valid */
        if (userDataRef != LUA_NOREF && userDataRef != LUA_REFNIL) {
            /* call test function */
            lua_getglobal(L, "test");
            lua_rawgeti(L, LUA_REGISTRYINDEX, userDataRef);
    
            /* free the registry slot (if you are done) */
            luaL_unref(L, LUA_REGISTRYINDEX, userDataRef);
    
            if (lua_pcall(L, 1, 0, 0)) {
                std::cout << lua_tostring(L, -1) << '\n';
                lua_pop(L, 1);
            }
        }
        lua_close(L);
    }
    

    也许你想看看 Sol2 Lua-C-API的包装器。它可以用最少的样板做你想做的事。但是,它需要C++ 14。

    #include <iostream>
    
    #define SOL_CHECK_ARGUMENTS 1
    #include <sol.hpp>
    
    extern "C" int luaopen_my(lua_State *L);
    
    int main() {
        sol::state L;
        L.open_libraries();
        luaopen_my(L);
    
        /* chunk A */
        L.script("local vec2 = my.Vec2(3, 4)\n"
                 "function setup()\n"
                   "return vec2\n"
                 "end\n");
        /* chunk B */
        L.script("function test(p)\n"
                   "print(p.x)\n"
                 "end\n");
    
        auto userDataRef = L["setup"]();
        L["test"](userDataRef);
    }
    
        2
  •  6
  •   Vlad    6 年前

    当获取指向userdata数据的原始指针并将其作为lightuserdata推送到参数时,将丢失有关对象类型的所有信息。lightuserdata甚至没有单独的元表。

    #include <iostream>
    #include <lua.hpp>
    
    extern "C"
    {
        int luaopen_my(lua_State *L);
    }
    
    int main()
    {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
    
        /* chunk A */
        luaL_dostring(L, "local vec2 = {x=3, y=4}\n"
                         "function setup()\n"
                           "return vec2\n"
                         "end\n");
        /* chunk B */
        luaL_dostring(L, "function test(p)\n"
                           "print(p.x)\n"
                         "end\n");
    
        /* call setup function */
        int top = lua_gettop(L);
        lua_getglobal(L, "setup");
    
        if (lua_pcall(L, 0, LUA_MULTRET, 0))
        {
            std::cout << lua_tostring(L, -1) << '\n';
            lua_pop(L, 1);
    
            exit(EXIT_FAILURE); // simpy fail for demo
        }
    
        /* check the return value */
        if (lua_gettop(L) - top)
        {
            // the top now contains the value returned from setup()
    
            /* call test function */
            lua_getglobal(L, "test");
    
            // copy the original value as argument
            lua_pushvalue(L, -2);
    
            if (lua_pcall(L, 1, 0, 0))
            {
                std::cout << lua_tostring(L, -1) << '\n';
                lua_pop(L, 1);
                exit(EXIT_FAILURE);
            }
    
             // drop the original value
            lua_pop(L, 1);
    
        }else
        {
            // nothing is returned, nothing to do
        }
        lua_close(L);
    }