C++ Lua 5.2-C++;对象中的对象(使用lua_lightuserdata)

C++ Lua 5.2-C++;对象中的对象(使用lua_lightuserdata),c++,object,lua,metatable,lua-api,C++,Object,Lua,Metatable,Lua Api,编辑:[答案2中的解决方案] 我是LUA的新手,在尝试做我想做的事情时遇到了困难。我有一个C++对象,看起来像: C++对象定义 struct TLimit { bool enabled; double value; TLimit() : enabled(false), value(0.0) {} ~TLimit() {} }; class TMeaurement { public: TMeasurement() : meas(0.0) {}

编辑:[答案2中的解决方案]

我是LUA的新手,在尝试做我想做的事情时遇到了困难。我有一个C++对象,看起来像:

C++对象定义

struct TLimit
{
    bool   enabled;
    double value;

    TLimit() : enabled(false), value(0.0) {}
    ~TLimit() {}
};

class TMeaurement
{
public:
    TMeasurement() : meas(0.0) {}
    ~TMeasurement() {}

    TLimit min;
    TLimit max;
    double meas;
};
//-----------------------------------------------------------------------------
#define LUA_METAPOINTER     "itse.metapointer"    //-- Name given to the metatable for all lightuserdata (instances of LMetaPointer in C++)
//-----------------------------------------------------------------------------

class LMetaPointer
{
private:
    static lua_State*                           m_lua;           //-- All LMetaPointers will share a common lua State
    static const luaL_Reg                       m_lmembers[];    //-- Member functions (for later expansion)
    static const luaL_Reg                       m_lfunctions[];  //-- Metamethods
    static std::map<LMetaPointer*, std::string> m_pointers;      //-- List of all LMetaPointer instances

    std::string m_name;                  //-- Name of LUA global variable pointing to me.

    static int __index(lua_State* L);    //-- Shall be mapped to __index metamethod of the metatable for all lightuserdata pointers
    static int __newindex(lua_State* L); //-- Shall be mapped to __newindex metamethod of the metatable for all lightuserdata pointers

    void initialize(lua_State* L);       //-- Creates the metatable (only once) and publishes it

protected:
public:
    LMetaPointer(lua_State* L);
    virtual ~LMetaPointer();

    inline lua_State*  lua()    { return m_lua;             }
    inline std::string global() { return m_name;            }
    inline size_t      size()   { return m_pointers.size(); }

    void setGlobal(std::string n);      //-- Shall make this pointer globally accessible to LUA

    virtual int get(lua_State* L) = 0;  //-- To be implemented by inherited classes
    virtual int set(lua_State* L) = 0;  //-- To be implemented by inherited classes

    LMetaPointer* operator [](std::string n);
};
我希望能够在LUA中以以下形式访问TMeasurement类型的对象:

LUA所需用途

-- objmeas is an instance of TMeasurement
objmeas.min.enabled = true
print(objmeas.min.value);
print(step.meas);        -- Ok
print(step.min.enabled); -- Ok
print(step.min.enabled); -- Error: attempt to index field 'min' (a nil value)
…等等

另一件事,我不希望LUA为TMeasurement类型的对象的实例分配内存。这将在我的C++代码中完成。我尝试过很多不同的事情,都没有成功。我现在将发布我的最后一次尝试

在我的C++代码中,我定义了如下:

t限制-获取将映射到索引的函数

#define LUA_MEAS_LIMIT    "itse.measurement.limit"

extern int llim_get(lua_State* L)
{
    TLimit*     lim = (TLimit*)lua_chekuserdata(L, 1, LUA_MEAS_LIMIT);
    std::string key = std::string(luaL_checkstring(L, 2));

    //-- this is only to check what is going on
    std::cout << "lim.get: " << key << std::endl;

    if(key.find("enabled") == 0)
        lua_pushboolean(L, lim->enabled);
    else if(key.find("value") == 0)
        lua_pushnumber(L, lim->value);
    else
        return 0;   //-- should return some sort of error, but let me get this working first

    return 1;
}
extern int llim_set(lua_State* L)
{
    TLimit*     lim = (TLimit*)lua_chekuserdata(L, 1, LUA_MEAS_LIMIT);
    std::string key = std::string(luaL_checkstring(L, 2));

    //-- this is only to check what is going on
    std::cout << "limit.set: " << key << " <-" << std::endl;

    if(key.find("enabled") == 0)
        lim->enabled = lua_toboolean(L, 3);
    else if(key.find("value") == 0)
        lim->value = lua_tonumber(L, 3);

    return 0;
}
#define LUA_MEASUREMENT    "itse.measurement"

extern int lmeas_get(lua_State* L)
{
    TMeasurement* test = (TMeasurement*)lua_checkuserdata(L, 1, LUA_MEASUREMENT);
    std::string   key  = std::string(luaL_checkstring(L, 2));

    //-- this is only to check what is going on
    std::cout << "meas." << key << " ->" << std::endl;

    if(key.find("meas") == 0)
        lua_pushinteger(L, test->meas);
    else if(key.find("min") == 0)
    {
        lua_pushlightuserdata(L, &test->min);
        luaL_getmetatable(L, LUA_MEAS_LIMIT);
        lua_setmetatable(L, -2);
    }
    else if(key.find("max") == 0)
    {
        lua_pushlightuserdata(L, &test->max);
        luaL_getmetatable(L, LUA_MEAS_LIMIT);
        lua_setmetatable(L, -2);
    }
    else
        return 0;  //-- should notify of some error... when I make it work

    return 1;
}
此脚本生成的输出为:

                              first script line: print(step.meas);
meas.meas ->                     this comes from lmeas_get function
4.5                              this is the actual print from lua sentence
                              second script line: print(step.min.enabled)
meas.min ->                      accessing step.min, call to function lmeas_get
limit.get: enabled ->            accessing min.enabled, call to function llim_get
false                            actual print from script sentence
                              third script line: print(step.min.enabled)
limit.get: min ->                accessing min from limit object, call to llim_get ???????
所以。在我第一次访问字段“min”(即“max”)后,任何后续访问该字段的尝试都将返回“尝试访问索引…”错误。不管我是先访问_索引(locale=step.min.enabled)函数还是_newindex函数(step.min.enabled=true)

第一次访问objectstep的minmetatble时,我似乎把LUA堆栈搞乱了。它以某种方式“替换”了从LUA_度量元表到LUA_度量极限的“步骤指针”。。。我只是不知道为什么

请帮忙。。。是什么让我搞砸了这么多


谢谢你,很抱歉发了这么长的帖子。。。我只是不知道如何缩短它。

正如评论中已经提到的,所有lightuserdata共享一个元表(请参阅),因此所有lightuserdata值在任何时候都被完全相同地对待。如果更改一个lightuserdata的元表,则所有的元表都会更改。这就是代码中发生的情况:

  • LEngine::runScript
    中,您使所有lightuserdata的行为类似于
    TMeasurement
    对象。对于全局变量
    step
    中的值,这是正常的
  • 当您第一次访问
    step.min
    时,会使所有lightuserdata的行为类似于
    TLimit
    对象(在
    lmeas\u get
    中)。这对于
    step.min
    推送的值是可以的,但是现在
    step
    中的值的行为也类似于
    TLimit
    ,因此
  • 当您第二次尝试访问
    step.min
    时,
    step
    充当
    TLimit
    对象,因此它没有字段
    min
    ,并返回
    nil
  • Lightuserdata根本不是适合该作业的工具。例如,有关可以使用lightuserdata的情况,请参见。对于其他一切,您都需要完整的用户数据。与lightuserdata相比,这将分配一些额外的内存(很抱歉,这没有帮助),但您可以进行一些缓存以避免生成太多临时数据


    因此,对于您的
    步骤
    值,您使用一个完整的用户数据,其中包含一个指向
    TMeasurement
    对象的指针。您还将一个新表设置为uservalue(请参阅),它将充当子userdata的缓存。当使用
    “min”/“max”
    参数调用
    lmeas\u get
    时,使用相同的键查看uservalue表。如果找不到此字段的预先存在的userdata,则创建一个新的完整userdata,其中包含指向
    TLimit
    子对象的指针(使用适当的元表),将其放入缓存中,然后返回它。如果您的对象生命期将来变得更加复杂,您应该将
    TLimit
    子用户数据中的反向引用添加到父
    TMeasurement
    userdata,以确保后者不会被垃圾收集,直到对前者的所有引用都消失为止。您也可以使用uservalue表来实现这一点。

    首先,感谢@siffiejoe和@greatwolf的帖子。是他们向我解释了我做错了什么

    现在,我的解决方案。我很确定这个解决方案不是最好的,但它满足了我目前的需求。如果有任何人有任何建议,请查看/查找潜在的bug,或者简单地发表评论

    解决方案-想法

    因为在LUA中,所有lightuserdata都共享同一个元表,所以我决定让我想要传递lightuserdata指针到LUA的所有结构和类都从我称为
    LMetaPointer
    的公共类继承。此类将发布一个元表,并将
    \uuu索引
    \uu新索引
    映射到给定的静态方法
    LMetaPointer::\uu索引
    LMetaPointer::\uu新索引
    。该类还包含一个指向所有创建的
    LMetaPointer
    实例的指针的
    static std::map
    (列表)。类的构造函数确保将新创建的实例添加到此映射

    在lua中,只要调用元方法
    \uuuu index
    \uu newindex
    ,就会执行相应的
    LMetaPointer::\uu index
    LMetaPointer::\uu newindex
    。此方法在映射中搜索负责方法调用的相应指针,并调用其自己的
    get
    set
    方法,这些方法在
    LMetaPointer
    类中定义为纯虚拟

    我知道这可能有点混乱,所以我现在将发布class
    LMetaPointer

    解决方案-框架:LMetaPointer类

    struct TLimit
    {
        bool   enabled;
        double value;
    
        TLimit() : enabled(false), value(0.0) {}
        ~TLimit() {}
    };
    
    class TMeaurement
    {
    public:
        TMeasurement() : meas(0.0) {}
        ~TMeasurement() {}
    
        TLimit min;
        TLimit max;
        double meas;
    };
    
    //-----------------------------------------------------------------------------
    #define LUA_METAPOINTER     "itse.metapointer"    //-- Name given to the metatable for all lightuserdata (instances of LMetaPointer in C++)
    //-----------------------------------------------------------------------------
    
    class LMetaPointer
    {
    private:
        static lua_State*                           m_lua;           //-- All LMetaPointers will share a common lua State
        static const luaL_Reg                       m_lmembers[];    //-- Member functions (for later expansion)
        static const luaL_Reg                       m_lfunctions[];  //-- Metamethods
        static std::map<LMetaPointer*, std::string> m_pointers;      //-- List of all LMetaPointer instances
    
        std::string m_name;                  //-- Name of LUA global variable pointing to me.
    
        static int __index(lua_State* L);    //-- Shall be mapped to __index metamethod of the metatable for all lightuserdata pointers
        static int __newindex(lua_State* L); //-- Shall be mapped to __newindex metamethod of the metatable for all lightuserdata pointers
    
        void initialize(lua_State* L);       //-- Creates the metatable (only once) and publishes it
    
    protected:
    public:
        LMetaPointer(lua_State* L);
        virtual ~LMetaPointer();
    
        inline lua_State*  lua()    { return m_lua;             }
        inline std::string global() { return m_name;            }
        inline size_t      size()   { return m_pointers.size(); }
    
        void setGlobal(std::string n);      //-- Shall make this pointer globally accessible to LUA
    
        virtual int get(lua_State* L) = 0;  //-- To be implemented by inherited classes
        virtual int set(lua_State* L) = 0;  //-- To be implemented by inherited classes
    
        LMetaPointer* operator [](std::string n);
    };
    
    以及不同类的不同方法的定义

    int LMeasLimit::get(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("enabled") == 0)
            lua_pushboolean(L, enabled);
        else if(key.find("value") == 0)
            lua_pushnumber(L, value);
        else
            return 0;
    
        return 1;
    }
    //-----------------------------------------------------------------------------
    
    int LMeasLimit::set(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("enabled") == 0)
            enabled = lua_toboolean(L, 3);
        else if(key.find("value") == 0)
            value = lua_tonumber(L, 3);
    
        return 0;
    }
    //-----------------------------------------------------------------------------
    
    
    
    
    int LMeasurement::get(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("value") == 0)
            lua_pushnumber(L, value);
        else if(key.find("result") == 0)
            lua_pushunsigned(L, result);
        else if(key.find("message") == 0)
            lua_pushstring(L, message.c_str());
        else
            return 0;
    
        return 1;
    }
    //-----------------------------------------------------------------------------
    
    int LMeasurement::set(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("value") == 0)
            value = lua_tonumber(L, 3);
        else if(key.find("result") == 0)
            result = LStepResult(lua_tounsigned(L, 3));
        else if(key.find("message") == 0)
            message = std::string(lua_tostring(L, 3));
    
        return 0;
    }
    //-----------------------------------------------------------------------------
    
    
    
    int LTest::get(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("id") == 0)
            lua_pushinteger(L, id);
        else if(key.find("name") == 0)
            lua_pushstring(L, name.c_str());
        else if(key.find("min") == 0)
        {
            lua_pushlightuserdata(L, &min);
            luaL_getmetatable(L, LUA_METAPOINTER);
            lua_setmetatable(L, -2);
        }
        else if(key.find("max") == 0)
        {
            lua_pushlightuserdata(L, &max);
            luaL_getmetatable(L, LUA_METAPOINTER);
            lua_setmetatable(L, -2);
        }
        else if(key.find("meas") == 0)
        {
            lua_pushlightuserdata(L, &meas);
            luaL_getmetatable(L, LUA_METAPOINTER);
            lua_setmetatable(L, -2);
        }
        else
            return 0;
    
        return 1;
    }
    //-----------------------------------------------------------------------------
    
    int LTest::set(lua_State* L)
    {
        std::string key = std::string(luaL_checkstring(L, 2));
    
        if(key.find("id") == 0)
            id = lua_tointeger(L, 3);
        else if(key.find("name") == 0)
            name = std::string(lua_tostring(L, 3));
    
        return 0;
    }
    
    解决方案-将所有问题放在一起 最后的修改在原始问题的
    LEngine::runScript

    int LEngine::runScript(std::string scrName)
    {
        luaInit();
    
        if(m_lua)
        {
            LTest tst(m_lua);
    
            tst.name = std::string("mierda_esta");
            tst.setGlobal("step");
    
            if(luaL_loadfile(m_lua, scrName.c_str()) || lua_pcall(m_lua, 0, 0, 0))
                processLuaError();
        }
    
        return 0;
    }
    
    最后,我将展示一个用于测试的LUA脚本及其输出

    测试-LUA脚本

    print("step.id          = " .. step.id)
    print("step.name        = " .. step.name)
    print(step.min.enabled)
    print("step.min.value   = " .. step.min.value)
    
    
    step.id = 1
    step.name = "nombre del test";
    step.min.enabled = true;
    step.min.value   = 5.6;
    
    print("step.id          = " .. step.id)
    print("step.name        = " .. step.name)
    print(step.min.enabled)
    print("step.min.value   = " .. step.min.value)
    
    step.id          = 0
    step.name        = mierda_esta
    false
    step.min.value   = 0
    step.id          = 1
    step.name        = nombre del test
    true
    step.min.value   = 5.6
    
    测试-输出

    print("step.id          = " .. step.id)
    print("step.name        = " .. step.name)
    print(step.min.enabled)
    print("step.min.value   = " .. step.min.value)
    
    
    step.id = 1
    step.name = "nombre del test";
    step.min.enabled = true;
    step.min.value   = 5.6;
    
    print("step.id          = " .. step.id)
    print("step.name        = " .. step.name)
    print(step.min.enabled)
    print("step.min.value   = " .. step.min.value)
    
    step.id          = 0
    step.name        = mierda_esta
    false
    step.min.value   = 0
    step.id          = 1
    step.name        = nombre del test
    true
    step.min.value   = 5.6
    
    所以,现在一切似乎都按照我的意愿运行了。我仍然需要修改此
    LMetaPointer
    ,以便现在能够调用任何