简体   繁体   中英

Trying to load penlight lua modules from C

I am beginning to put my hands in lua and have had a quite frustrating experience so far. I need to load portions of penlight, in order to load "mine.lua" that requires pl.import_into. I need to do this from C/C++.

I am using lua 5.1; using 5.2 is not an option atm so I don't have luaL_requiref

Roughly my code looks like this:

void luaLoad(lua_State* L, const char* f) {
  if (luaL_dofile(L, f) || lua_pcall(L, 0, 0, 0)) {
    const char* err = lua_tostring(L, -1);
    lua_pop(L, -1);
    throw std::runtime_error("Lua load error");
  }
}
int main(void) {
  lua_State *L = luaL_newstate();
  luaL_openlibs(L);
  luaLoad(L, "~/Penlight-1.3.1/lua/pl/compat.lua");
  luaLoad(L, "~/Penlight-1.3.1/lua/pl/utils.lua");
  luaLoad(L, "~/Penlight-1.3.1/lua/pl/import_into.lua");
  luaLoad(L, "mine.lua");
  ...
}

I started trying to load import_into.lua, which required utils.lua and transitively compat.lua.

In my luaLoad method, if I drop the lua_pcall, utils.lua "does not see" compat.lua:

utils.lua : module 'pl.compat' not found`

With the lua_pcall however I get an

attempt to call a table value` error.

Is trying to load penlight from C fundamentally wrong ?

First: The lua_pcall is superfluous. luaL_dofile already does a lua_pcall . The lua_pop call is also wrong. Second: Any reason why you don't just modify package.path , so that Lua's require function can find the necessary modules?

If you can't or don't want to do that, there are two approaches:

In addition to running the module code you also need to store its result in the package.loaded table using the module name as the key. This way the require function called in the penlight code can find those modules later.

void luaLoad(lua_State* L, char const* m, const char* f) {
  int top = lua_gettop(L);
  if (luaL_dofile(L, f)) {
    const char* err = lua_tostring(L, -1);
    lua_pop(L, 1); /* the 2nd parameter is a number not a stack index! */
    throw std::runtime_error("Lua load error");
  } else {
    lua_settop(L, top+1); /* discard anything but first return value */
    lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); /* get `package.loaded` */
    lua_pushvalue(L, -2); /* `lua_setfield` needs the return value at stack top */
    lua_setfield(L, -2, m); /* store return value as `package.loaded[m]` */
    lus_settop(L, top); /* restore Lua stack */
  }
}

Advantages are that you can use the same function to run your mine.lua . The downside is that you have to load the modules in the correct order, and if one of the modules uses the module function, the code gets a little more complicated (basically you are reimplementing parts of Lua's require function).

The second approach is to load but not run the modules and put the loaded code (which is a Lua function) in the package.preload table using the module name as the key. The require function can later pick them up and run them from there.

void luaPreload(lua_State* L, char const* m, char const* f) {
  if (luaL_loadfile(L, f)) {
    char const* err = lua_tostring(L, -1);
    lua_pop(L, 1);
    throw std:runtime_error("Lua load error");
  } else {
    lua_getglobal(L, "package"); /* there is no shorthand for `package.preload` */
    lua_getfield(L, -1, "preload");
    lua_pushvalue(L, -3); /* `lua_setfield` needs the function at stack top */
    lua_setfield(L, -2, m); /* store chunk as `package.preload[m]` */
    lua_pop(L, 3); /* restore Lua stack */
  }
}

Advantages are that require will run the modules in the correct order automatically, and the module function doesn't need special handling. A disadvantage is that you need separate code to run mine.lua .

All those approaches assume that mine.lua uses Lua's require function to access the penlight modules.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM