经过多次尝试和数小时的谷歌搜索,我终于意识到SOF可能是我解决这个问题的最佳场所。
我目前正在使用Lua C API创建扩展,这是一个可以通过require
要求使用的共享库。我正在尝试做一些与此非常相似的事情(抱歉格式化):
local data = {
something = "some"
}
local rpc = {}
function rpc.method()
print('ran')
end
function rpc.method2()
print('ran222')
end
local metatable = {}
metatable.__index = function(self, key)
return data[key]
end
setmetatable(rpc, metatable)
--------------------------------------------
rpc.method()
print(rpc['something'])
rpc.method2()
我当前的C代码看起来像这样:
static int lua_index(lua_State* lua) { //idk
std::cout << "Was indexed";
const char* a = luaL_checkstring(lua, 2);
std::cout << a << std::endl;
return 0;
}
static int lua_initialize(lua_State* lua) {
std::cout << "ran";
return 0;
}
static const struct luaL_Reg lib[] = {
{ "initialize", lua_initialize },
{"__index", lua_index},
{ NULL, NULL }
};
extern "C" int DISCORD_RPC_EXPORT luaopen_DiscordRPC(lua_State* lua) {
luaL_newlib(lua, lib);
lua_setmetatable(lua, 1);
return 1;
}
以此为例:
local rpc = require "DiscordRPC"
print(rpc['a'])
rpc.initialize();
现在显然rpc['a']
会返回nil(肯定lua_index
确实会运行)因为lua_index
没有做任何事情,但为什么我不能调用initialize
?它显然在那里。它说:lua: main.lua:5: attempt to call a nil value (field 'initialize')
所以它是零。如果我要删除lua_setmetatable(lua, 1);
我可以调用该函数,但我无法将其DiscordRPC
编入索引。另外我想指出的是,当我删除它时,__index
metamethod会被调用两次,显然它会。
我的问题:我怎样才能做到这一点?我试图在表上设置metatable并返回整个metatable。 任何帮助都非常感谢,因为我完全迷失了。谢谢。
答案 0 :(得分:1)
您必须通过将表推入栈顶然后调用lua_setmetatable
来将元表设置为表。
luaL_newlib(lua, lib);
lua_pushvalue(lua, -1);
lua_setmetatable(lua, -2);
对lua_pushvalue
的调用会将位置-1(堆栈顶部)上的值再次推入堆栈,从而进行复制。然后,我们调用lua_setmetatable
(此处的文档doc)以在堆栈顶部弹出一个表,并将其设置为给定索引(-2,即我们的库,已推送)中的表的元表。通过luaL_newlib
命令)。
这样做并运行您的示例,我得到了以下结果:
local rpc = require "DiscordRPC"
print(rpc['a'])
-- First, calls the __index function, printing
-- 'Was indexeda'
-- Then prints nil, since the key 'a' holds nothing
rpc.initialize();
-- Prints ran
如果您对此还有更多疑问,建议您阅读Lua Book编程中有关Userdata and Object-Oriented Access的章节。