在Lua中的package.preload替代方案?

时间:2018-07-17 13:44:40

标签: c++ lua

我了解到package.preload可用于将一个脚本公开给其他脚本。

这是我的示例代码。

lua_State *L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
//Script A
luaL_dostring(L, "local A = {} package.preload['A'] = function () return A end A.num = 3"); 
//Script B
luaL_dostring(L, "local A = require 'A' print(A.num)"); 
lua_close(L);

结果:3

尽管这可以很好地工作,但我想知道是否可以更简化脚本A的代码,或者是否存在其他替代解决方案将脚本公开给其他脚本。

添加:我问这的主要原因是因为我认为package.preload['A'] = function () return A end相当长而且很无聊。

2 个答案:

答案 0 :(得分:3)

在这种情况下,如果您有一些表示Lua模块的in-C字符串集,则package.preload正是要使用的工具。尽管您对它的特定用法仍有待改进。

通常来说,模块本身并不定义其名称。因此,将模块名称硬编码到字符串中并不是正确的做法。同样,模块不会自行注册。它们应该由模块周围的环境注册。

您真正想要的是获取一个name + Lua代码字符串数组,并将其注册为循环中的模块预加载。所以你会有这样的事情。我将使用Lua 5.3;您可以轻松地将其翻译为旧版本的Lua。

还请注意:此代码未经测试。

const char *lua_preloads[] =
{
    "A", "local A = {}\n"
        "A.num = 3\n"
        "return A)\n", //Modules are usually tables, not functions.
    ...
    NULL //Null-terminated list.
};

//Loader function
int lua_preloader_func(lua_State *L)
{
    int nargs = lua_gettop(L);
    int lua_func_ix = lua_upvalueindex(1);
    lua_pushvalue(L, lua_func_ix);

    //Move the function to the bottom of the stack
    lua_insert(lua_func_ix, 1);

    //Call with all of the given arguments.
    lua_call(L, nargs, LUA_MULTRET);
    return lua_gettop(L);
}

int top = lua_gettop(L);

//Get the package.preload table.
lua_getglobal(L, "package");
lua_getfield(L, -1, "preload");
int preload_ix = lua_gettop();

for(const char **position = lua_preloads;
    *position;
    position += 2)
{
    const char *module_name = position[0];
    const char *module = position[1];

    //Compile the preload script into a Lua function.
    int err = luaL_loadbufferx(L, module, strlen(module), module_name, "t");

    //Check for errors in `err`.

    //Create a Lua C-function with the script as an upvalue.
    lua_pushcclosure(L, lua_preloader_func, 1);

    //Stick that Lua C-function inside of package.preload[preload.first].
    lua_setfield(L, preload_ix, module_name);
}

lua_settop(L, top);

答案 1 :(得分:1)

似乎您想为每个定义模块的块加上local A = {} package.preload['A'] = function () return A end前缀(其中A是模块名称)。我认为仅使用字符串连接会容易得多。

#include <string>
#include <lua.hpp>

int preload(lua_State *L, std::string const &modname,
            std::string const &modcode) {
    std::string code = "package.preload['" + modname + "'] = function()\n" +
                       "local " + modname + " = {}\n" + modcode + "\n"
                       "return " + modname + " end";
    return luaL_dostring(L, code.c_str());
}

int main() {
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);

    // Script A
    preload(L, "A", "A.num = 3");
    // Script B
    luaL_dostring(L, "local A = require 'A' print(A.num)");
    lua_close(L);
}