6
votes

Issue

I want to call a Lua script which require()s lyaml module, Lua binding for LibYAML, from a C program.

I compiled Lua 5.2 from source and I hacked the module to make it work with Lua 5.2. It can be found on github.

The Lua script follows, it works either with Lua 5.1 and 5.2:

-- foo.lua
require('lyaml')

function hello ()
  res = lyaml.load("a: 4\n")
  return res.a
end

-- then calling hello() it works like a charm
print( hello() ) -> 4


Problem

I wrote a C program which should call hello() from the script, following Programming in Lua, Chapter 25 and Lua 5.2 Reference Manual.

The C program follows:

/* foo.c */
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

int main(void)
{
  double z;

  lua_State *L = luaL_newstate();
  luaL_openlibs(L);

  if (luaL_dofile(L, "foo.lua"))
    luaL_error(L, "error running script: %s", lua_tostring(L, -1));

  lua_getglobal(L, "hello");

  if (lua_pcall(L, 0, 1, 0) != 0)
    luaL_error(L, "error calling hello: %s", lua_tostring(L, -1));

  if (!lua_isnumber(L, -1))
    luaL_error(L, "result must be number");        

  z = lua_tonumber(L, -1);
  lua_pop(L, 1);

  lua_close(L);
  return 0;
}

I compile issuing:

gcc -Wall -o foo foo.c -ldl -lm -llua

Then when running foo, I receive at runtime the following error:

PANIC: unprotected error in call tu Lua API (
    error running script: error loading module 'lyaml' from file '/path/to/lyaml.so':
       /path/to/lyaml.so: undefined symbol: lua_gettop)
Aborted

So I tried to load lyaml from the C program, adding the following line after luaL_openlibs() call:

luaL_requiref(L, "lyaml", luaopen_package, 1);

After recompilation the error becomes:

PANIC: unprotected error in call tu Lua API (
    error running script: 
       hello.lua:4: attempt to index global 'lyaml' (a nil value))
Aborted

So I imagine that there's no lyaml symbol and the require() call fails somehow.

By reading luaL_requiref() documentation I thought modname would be set by its call setting glb flag to true:

void luaL_requiref (lua_State *L, const char *modname, 
                    lua_CFunction openf, int glb);

Calls function openf with string modname as an argument and sets the call result in package.loaded[modname], as if that function has been called through require.

If glb is true, also stores the result into global modname.
Leaves a copy of that result on the stack.

I tried to comment the require() call in Lua script and the result is the same.

Question

What did I do wrong? Am I forgetting to do something?


EDIT

I hacked the module updating deprecated (removed) functions/types with their substitute as follows:

lua_strlen() -> luaL_len()
luaL_reg     -> luaL_Reg
luaL_getn()  -> luaL_len()

However Lua scripts using lyaml work so I think the problem is not my hack.

I tried original lyaml module with Lua 5.1. Results are the same, so I'm sure the problem is not my hack.

UPDATE

Adding the following line, as suggested by Doug Currie in his answer, the C program works perfectly with Lua 5.1. I still get the same error in 5.2 though.

lyaml = require('lyaml')
2
"I hacked the module to make it work with Lua 5.2." Did you "hack" it correctly? If you're not using the original module, then there's no way to know that it works correctly without seeing your actual code. Also, what does Luabind have to do with this? Does lyaml use that?Nicol Bolas
@NicolBolas sorry for [luabind] I did not mention to use it. I added further explanation about the hack.dave
@NicolBolas As I reported in the update, I ran the same scenario with Lua 5.1 and the original lyaml module. Results are the same. I would say that it would be good practice, and matter of respect, to ask further information about issues before voting close. Thank you.dave

2 Answers

6
votes

foo.lua as written will only run in Lua 5.1.x -- your hacked lyaml.c doesn't set the global lyaml and neither does require in Lua 5.2. I suspect your PATH did not have Lua 5.2 when you ran the first test "works like a charm," or else you set lyaml manually before you loaded foo.lua.

foo.lua should start with

lyaml = require('lyaml')
5
votes

I solved... the root of all problems was a bad use of luaL_requiref().

Reading this thread i got inspired by this follow up:

In Lua 5.2, libraries no longer create globals; they just return the library table.

luaL_openlibs calls luaL_requiref instead of calling luaopen_*, and luaL_requiref sets the corresponding globals.

so it is necessary to call luaL_requiref() to load the module and set the lyaml global.

As stated in the question I tried using

luaL_requiref(L, "lyaml", luaopen_package, 1);

but it's totally wrong because luaopen_package() is the loader function for Lua standard package module... I had to use instead:

luaL_requiref(L, "lyaml", luaopen_lyaml, 1);

and compile with

gcc -L/path/to/5.2 -Wall -o foo foo.c -ldl -lm -llua -l:lyaml.so.1

to get references to luaopen_lyaml().


In Lua 5.1, as Doug Currie said, it was enough to issue

lyaml = require('lyaml')

in hello.lua, without calling luaL_requiref() in C program.