C++ Lua 5.2-C++;对象中的对象(使用lua_lightuserdata)
编辑:[答案2中的解决方案] 我是LUA的新手,在尝试做我想做的事情时遇到了困难。我有一个C++对象,看起来像: C++对象定义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) {}
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
因此,对于您的
步骤
值,您使用一个完整的用户数据,其中包含一个指向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
类中定义为纯虚拟
我知道这可能有点混乱,所以我现在将发布classLMetaPointer
解决方案-框架: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
,以便现在能够调用任何