1

I wrote a simple C plugin for Lua:

#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static int bar (lua_State *L) {
  double arg1 = luaL_checknumber(L, 1);
  double arg2 = luaL_checknumber(L, 2);
  lua_Number res = arg1 + arg2;
  lua_pushnumber(L, res);
  return 1;
}

int luaopen_foo (lua_State *L) {
  static const struct luaL_Reg foo [] = {
    {"bar", bar},
    {NULL, NULL}
  };
  lua_newtable(L);
  luaL_setfuncs(L, foo, 0);
  lua_setglobal(L, "foo");
  return 1;
}

The code is compiled successfully with this GCC command:

gcc -W -Wall -g -fPIC -shared -I/usr/include/lua5.3 -o foobar.so foobar.c

In a Lua 5.3 REPL, I'm able to find and import the module successfully as well, but the returned value of the function call is always nil:

root@b1898c1cc270:/# lua5.3
Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> local foo = require "foo"
> local res = foo.bar(3.0, 6.0)
> res
nil

No errors are thrown and since I'm able to printf the result in the C code before returning the value, I know the code is called and the result calculated successfully.

Any ideas?

Edit1: By not using local variables I get this stack trace instead of a nil value:

root@d7340c919be4:/# lua5.3
Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio
> foo = require "foo"
> res = foo.bar(3.0, 6.0)
stdin:1: attempt to call a nil value (field 'bar')
stack traceback:
    stdin:1: in main chunk
    [C]: in ?
6
  • 1
    Don't use local in an interactive interpreter session. Commented Apr 6, 2018 at 10:10
  • 1
    Also, lua_setglobal pops your module table from the Lua stack, so you can't return 1 from your luaopen_foo function unless you duplicate the stack slot first with lua_pushvalue. Commented Apr 6, 2018 at 10:18
  • @siffiejoe Thanks for the quick answer. I edited my question with a stack trace I get when not using local variables. Any reasons to not use them in the interpreter (just being curious)? I'm using lua_setglobal because I thought this was the correct way to register a module in 5.3 since register and lua_register was deprecated in 5.2. How should the module be registered normally? Commented Apr 6, 2018 at 10:29
  • lua_open and lua_register* Commented Apr 6, 2018 at 10:37
  • 1
    Regarding modules: It is customary to not set globals in Lua modules any more but to use the return value of require (and thus of the luaopen_* function). So lua_newtable + luaL_setfuncs is fine, as is using luaL_newlib (which is roughly equivalent to the combo above). But if you want to set a global and return the module table from the luaopen_* function, you have to duplicate it on the stack first. Commented Apr 6, 2018 at 12:35

2 Answers 2

3

luaL_setfuncs just registers your functions into a table.

Instead, use luaL_newlib. It creates a new table and registers your functions there. Then you need to push the table on to lua stack.

luaL_newlib (L, foo);
return 1;
Sign up to request clarification or add additional context in comments.

Comments

1

As to what's causing the error, lua_setglobal pops the library table from the stack, so that luaopen_foo does not return it. Then (though I don't understand why) require "foo" returns the file path of the library (a string) instead, and the field ("foo_filepath").bar is nil.

So, removing the call to lua_setglobal fixes the problem, even if you do not use the library-creating macro luaL_newlib as Ravi recommends.

To set the library table as global foo and return it from require, you have to push a second copy of the table to the stack with lua_pushvalue(L, -1) before doing lua_setglobal(L, "foo"), leaving the original copy of the table to be returned by luaopen_foo.

1 Comment

Quote from the require manual entry: "Once a loader is found, require calls the loader with two arguments: modname and an extra value dependent on how it got the loader. (If the loader came from a file, this extra value is the file name.)". So the luaopen_* function starts with those two values on the stack. Once the module table is popped by lua_setglobal, the file name is the top-most element that's left on the stack.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.