编辑:[答案 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;
};
我希望能够在 LUA 中以下列形式访问 TMeasurement 类型的对象:
LUA 所需用途
-- objmeas is an instance of TMeasurement
objmeas.min.enabled = true
print(objmeas.min.value);
...ETC
另一件事,我不希望 LUA 为 TMeasurement 类型的对象的实例分配内存。这将在我的 C++ 代码中完成。我尝试了很多不同的方法,都没有成功。我现在将发布我的最后一次尝试。
在我的 C++ 代码中,我定义了以下内容:
TLimit - 获取将映射到 __index 的函数
#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;
}
TLimit - 设置将映射到 __newindex 的函数
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;
}
现在,TMeasurement 类又多了一个函数。(我不会在这个例子中提供成员“meas”的设置函数)。
TMeasurement - 获取 __index 的函数
#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;
}
现在,代码中为这两个对象创建元表的部分:
C++ - 发布元表
(不要介意 nsLUA::safeFunction<...> 位,它只是一个模板函数,它将以“安全模式”在 < > 中执行该函数......当出现错误时它会弹出一个 MessaegBox遭遇)
static const luaL_Reg lmeas_limit_f[] = { { NULL, NULL} };
static const luaL_Reg lmeas_limit[] =
{
{ "__index", nsLUA::safeFunction<llim_get> },
{ "__newindex", nsLUA::safeFunction<lllim_set> },
{ NULL, NULL }
};
//-----------------------------------------------------------------------------
static const luaL_Reg lmeas_f[] = { { NULL, NULL} };
static const luaL_Reg lmeas[] =
{
{ "__index", nsLUA::safeFunction<lmeas_get> },
{ NULL, NULL }
};
//-----------------------------------------------------------------------------
int luaopen_meas(lua_State* L)
{
//-- Create Measurement Limit Table
luaL_newmetatable(L, LUA_MEAS_LIMIT);
luaL_setfuncs(L, lmeas_limit, 0);
luaL_newlib(L, lmeas_limit_f);
//-- Create Measurement Table
luaL_newmetatable(L, LUA_MEASUREMENT);
luaL_setfuncs(L, lmeas, 0);
luaL_newlib(L, lmeas_f);
return 1;
}
最后,我在 C++ 中的主要函数,初始化 LUA,创建对象 TMeasurement 和实例,将其作为全局传递给 LUA 并执行 lua 脚本。大部分功能都包含在另一个名为 LEngine 的类中:
C++ - 主函数
int main(int argc, char* argv[])
{
if(argc < 2)
return show_help();
nsLUA::LEngine eng;
eng.runScript(std::string(argv[1]));
return 0;
}
//-----------------------------------------------------------------------------
int LEngine::runScript(std::string scrName)
{
//-- This initialices LUA engine, openlibs, etc if not already done. It also
// registers whatever library I tell it so by calling appropriate "luaL_requiref"
luaInit();
if(m_lua) //-- m_lua is the lua_State*, member of LEngine, and initialized in luaInit()
{
LMeasurement measurement;
measurement.value = 4.5; //-- for testing purposes
lua_pushlightuserdata(m_lua, &tst);
luaL_getmetatable(m_lua, LUA_MEASUREMENT);
lua_setmetatable(m_lua, -2);
lua_setglobal(m_lua, "step");
if(luaL_loadfile(m_lua, scrName.c_str()) || lua_pcall(m_lua, 0, 0, 0))
processLuaError(); //-- Pops-up a messagebox with the error
}
return 0;
}
现在,终于有问题了。当我执行任何 lua 脚本时,我可以访问 step 没问题,但我只能在第一次访问“min”或“max”内的 memebr ......任何后续访问都会出错。
LUA - 示例一
print(step.meas); -- Ok
print(step.min.enabled); -- Ok
print(step.min.enabled); -- Error: attempt to index field 'min' (a nil value)
此脚本生成的输出为:
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”)之后,任何后续访问它的尝试都将返回“尝试访问索引...”错误。我是先访问 __index (local e = step.min.enabled) 函数还是 __newindex 函数 (step.min.enabled = true) 都没有关系。
似乎我第一次访问对象步骤的 min metatble 时弄乱了 LUA 堆栈。它以某种方式“替换”了从 LUA_MEASUREMENT 元表到 LUA_MEAS_LIMIT 的“步骤指针”......我根本不知道为什么。
请帮忙......我搞砸了这么多是什么?
谢谢你,很抱歉这篇长文......我只是不知道如何让它更短。