如何在 C++ 中从一个 Lua chunk 传递 userdata 到另一个 chunk

我正在尝试在 C++ 中从 Lua 脚本 (chunk A) 中获取 userdata(通过函数返回变量)并将这个 userdata 传递回 C++,以便在之后的 Lua 脚本 (chunk B) 中使用它(通过函数参数)。这样 userdata 就能像在 chunk A 中一样在 chunk B 中使用。

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;

    /* 调用 setup 函数 */
    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);
    }
    /* 检查返回值 */
    if (lua_gettop(L) - top)
    {
        /* 将 userdata 存储到指针 */
        if (lua_isuserdata(L, -1))
            userDataPtr = lua_touserdata(L, -1);
    }
    /* 检查 userDataPtr 是否有效 */
    if (userDataPtr != nullptr)
    {
        /* 调用 test 函数 */
        lua_getglobal(L, "test");
        lua_pushlightuserdata(L, userDataPtr); /* 将 userdata 作为参数传递 */
        if (lua_pcall(L, 1, 0, 0))
        {
            std::cout << lua_tostring(L, -1) << '\n';
            lua_pop(L, 1);
        }
    }
    lua_close(L);
}

我得到的结果:

[string "local vec2 = my.Vec2(3, 4)..."]:6: attempt to index a userdata value (local 'p')

我期望的结果:

3

是否可能从 chunk A 获取 userdata 然后将其传递到 chunk B 中,使其像在 chunk A 中一样使用?

点赞
用户5675002
用户5675002

当你通过获取userdata的原始指针并将其作为 lightuserdata 传递给参数时,你将丢失有关对象类型的所有信息。即使是 lightuserdata,也没有独立的元表。

正确的方法是将 Lua 值按原样传递。将原始返回值保留在 Lua 栈上,或将其复制到其他 Lua 容器(例如临时 Lua 表或 Lua 注册表)中,然后将该值在 Lua 栈上复制以将其作为参数传递。这样你就不必了解有关绑定实现的任何信息。你甚至不必在意是否是 userdata 还是其他 Lua 类型。

根据你的代码,这可能像这样:

#include <iostream>
#include <lua.hpp>

extern "C"
{
    int luaopen_my(lua_State *L);
}

int main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    /* 脚本块 A */
    luaL_dostring(L, "local vec2 = {x=3, y=4}\n"
                     "function setup()\n"
                       "return vec2\n"
                     "end\n");
    /* 脚本块 B */
    luaL_dostring(L, "function test(p)\n"
                       "print(p.x)\n"
                     "end\n");

    /* 调用 setup 函数 */
    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); // 仅为演示而失败
    }

    /* 检查返回值 */
    if (lua_gettop(L) - top)
    {
        // 栈顶现在包含从 setup() 返回的值

        /* 调用 test 函数 */
        lua_getglobal(L, "test");

        // 将原始值作为参数传递
        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);
        }

         // 弹出原始值
        lua_pop(L, 1);

    }else
    {
        // 没有返回值,不需要做什么
    }
    lua_close(L);
}
2018-09-09 08:41:12