Kurieita
Kurieita

Reputation: 93

Lua C Extension: how to set metatable on new library

After multiple attempts and hours of googling I finally came to realize SOF is probably my best place to solve this.

I am currently using the Lua C API to create an extension, a shared library that you can use by requiring it via require. I am trying to do something very similar to this (sorry about the formatting):

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()

My current C code look something like this:

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;
}

And using this as an example:

local rpc = require "DiscordRPC"

print(rpc['a'])

rpc.initialize();

Now obviously rpc['a'] would return nil (yes lua_index does in fact run) because lua_index isn't doing anything but why can't I call initialize? It clearly there. It says: lua: main.lua:5: attempt to call a nil value (field 'initialize') So it is nil. If I would to remove lua_setmetatable(lua, 1); I would be able to call that function but I can not index it DiscordRPC as I want to. Also I would like to point out that when I remove it the __index metamethod get called two times, obviously it would.

My question: how can I achieve this? I am trying to set a metatable on a table and return the entire metatable. Any help is very much appreciated as I am complete lost. Thank you.

Upvotes: 1

Views: 1644

Answers (1)

You have to set the metatable to your table by pushing it to the top of the stack and then calling lua_setmetatable.

luaL_newlib(lua, lib);
lua_pushvalue(lua, -1);
lua_setmetatable(lua, -2);

The call to lua_pushvalue will push the value on position -1 (the top of the stack) to the stack again, duplicating it. Then, we call lua_setmetatable(Documentation here doc) to pop a table on top of the stack and set it as the metatable for the table in the given index (-2, that is our lib, pushed by the luaL_newlib command).

By doing this and running your example, I got the following results:

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

If you have more questions towards this, I suggest you to read the chapter about Userdata and Object-Oriented Access in Programming in Lua Book.

Upvotes: 1

Related Questions