Lua C API:类访问器

我使用 Lua 编写了一个访问 C ++ 类的 Lua 包装器,使用 userdata 在 Lua 中实现,参照了这篇文章:http://lua-users.org/wiki/BindingWithMembersAndMethods

我的 C++ 类的样子是这样的:

class GameObject
{
public:
    GameObject();
    ~GameObject();

    int             m_id;
    float           m_scale;
    const char*     m_path;
};

到现在为止,我在 Lua 中能这样做:

gameObject = GameObject.new()
gameObject.path = "test"
gameObject.scale = 2
local path = gameObject.path
local scale = gameObject.scale
print(scale)
print(path)

一切都工作得很好,除了打印 path 变量时:

console output

"set string" 行来自我在设置函数中放置的调试器:

int LuaGameObjectManager::set_string (lua_State *L, void *v)
{
    v = (void*)luaL_checkstring(L, 3);
    std::cout << "set string : " << (char*) v << std::endl;
    return 0;
}

所以我想我正在设置正确的值,问题必须发生在使用这个获取函数时:

int LuaGameObjectManager::get_string (lua_State *L, void *v)
{
    char * tmp = (char*)v;
    lua_pushstring(L, tmp );
    return 1;
}

这是我定义方法和元表的方式:

static const luaL_Reg methodsArray[] = {
    {"new",        New},
    {"load",       load},
    {0,0}
};

static const luaL_Reg metaArray[] = {
    {"__gc",       gc},
    {"__tostring", toString},
    {0, 0}
};

static LuaManager::Xet_reg_pre gettersArray[] = {
    {"scale",       get_int,    offsetof(GameObject, m_scale)   },
    {"path",        get_string, offsetof(GameObject, m_path)   },
    {0,0}
};

static LuaManager::Xet_reg_pre settersArray[] = {
    {"scale",       set_int,    offsetof(GameObject, m_scale)   },
    {"path",        set_string, offsetof(GameObject, m_path)   },
    {0,0}
};

那么,有人有想法为什么我打印出一些奇怪的值吗?这是个编码问题吗? 我知道您们需要更多的代码来了解我的问题并帮助我,但是注册 lua 方法和元表的整个部分相当冗长,所以告诉我您想要我发布哪些代码。

点赞
用户3677376
用户3677376

我看到两个问题。

  1. 在函数内部将(基本)局部变量赋值不会对外部产生影响。

    int LuaGameObjectManager::set_string (lua_State *L, void *v)
    {
        v = (void*)luaL_checkstring(L, 3);
    

    这里的 v 是一个局部变量,所以它不会改变 set_string 方法以外的任何东西。你的对象的内存布局如下:

                  v
                   \
    |  int  | float |pointer|
                        \
                         |c|c|c|c|c|...
    

    所以你可能想要做类似于

    *((char**)v) = luaL_checkstring(L, 3);
    

    但要注意 luaL_checkstring 返回的字符串是由 Lua 管理的,可以在任意时间进行垃圾回收,因此你应该将其复制(并清理先前的 m_path 值,否则会出现内存泄漏)。细节留给你自己处理。

  2. 你发布的“get_string”方法(并在你提供的链接的 wiki 文章中使用)假定正在处理内联字符数组。

                  v
                   \
    |  int  | float |c|c|c|c|c|...
    

    你的情况不同,因为你正在使用一个字符数组的第一个元素的指针(如上所述)。一个适当的实现应该是这样的:

    int LuaGameObjectManager::get_string (lua_State *L, void *v)
    {
        lua_pushstring(L, *((char**)v));
        return 1;
    }
    

    在这种情况下,lua_pushstring 已经为你创建了一个副本(最终将被 Lua 进行垃圾回收),因此你不必过多考虑。

2016-06-07 16:36:20