【问题标题】:Calling lua functions from .lua's using handles?使用句柄从.lua 调用lua 函数?
【发布时间】:2011-03-26 19:46:26
【问题描述】:

我正在做一个尝试将 lua 与 c++ 集成的小项目。 然而,我的问题如下:

我有多个 lua 脚本,我们称它们为 s1.lua s2.lua 和 s3.lua。其中每一个都有以下函数:setVars() 和 executeResults()。

现在我可以通过 LuaL_dofile 调用 lua 文件,并在使用 setVars() 和/或 executeResults() 后立即调用。然而这里的问题是,在我加载 s2.lua 之后,我不能再调用 s1.lua 的函数。这意味着我必须重做 s1.lua 上的 LuaL_dofile 才能重新获得对该函数的访问权限,这样做我将无法访问 s2.lua 中的函数。

有没有办法简单地连续加载所有lua文件,然后开始随意调用它们的函数?类似 s1->executeResults() s5->executeResults() s3->setVars() 等

我目前已经有一个系统使用 boost::filesystem 来检测文件夹中的所有 lua 文件,然后我将这些文件名保存在一个向量中,然后简单地遍历该向量以连续加载每个 lua 文件。

放弃用 lua 文件名填充向量,我的插件加载函数现在看起来像这样:

void Lua_plugin::load_Plugins(){
 std::vector<std::string>::const_iterator it;
 for (it=Lua_PluginList.begin(); it!=Lua_PluginList.end(); it++){
  std::cout<<"File loading: " << *it << std::endl;
  std::string filename =  *it;
  std::string filepath = scriptdir+filename;
  if (luaL_loadfile(L, filepath.c_str()) || lua_pcall(L, 0, 0, 0)) {
   std::cout << "ScriptEngine: error loading script. Error returned was: " << lua_tostring(L, -1) << std::endl;
  }
 }
}

为了更清楚一点,我在 .lua 中的所有内容都是这样的:

-- s1.lua

setVars()
--do stuff
end

executeResults()
--dostuff
end

等,但我希望能够在简单地连续加载两者之后调用 s1.lua 的 setVars() 和 s2.lua 的 setVars()。

【问题讨论】:

    标签: c++ lua lua-api


    【解决方案1】:

    这实际上是 gwell 使用 C API 提出的:

    #include <stdio.h>
    
    #include "lua.h"
    
    static void
    executescript(lua_State *L, const char *filename, const char *function)
    {
        /* retrieve the environment from the resgistry */
        lua_getfield(L, LUA_REGISTRYINDEX, filename);
    
        /* get the desired function from the environment */
        lua_getfield(L, -1, function);
    
        return lua_call(L, 0, 0);
    }
    
    static void
    loadscript(lua_State *L, const char *filename)
    {
        /* load the lua script into memory */
        luaL_loadfile(L, filename);
    
        /* create a new function environment and store it in the registry */
        lua_createtable(L, 0, 1);
        lua_getglobal(L, "print");
        lua_setfield(L, -2, "print");
        lua_pushvalue(L, -1);
        lua_setfield(L, LUA_REGISTRYINDEX, filename);
    
        /* set the environment for the loaded script and execute it */
        lua_setfenv(L, -2);
        lua_call(L, 0, 0);
    
        /* run the script initialization function */
        executescript(L, filename, "init");
    }
    
    int
    main(int argc, char *argv[])
    {
        lua_State *L;
        int env1, env2;
    
        L = (lua_State *) luaL_newstate();
        luaL_openlibs(L);
    
        loadscript(L, "test1.lua");
        loadscript(L, "test2.lua");
    
        executescript(L, "test1.lua", "run");
        executescript(L, "test2.lua", "run");
        executescript(L, "test2.lua", "run");
        executescript(L, "test1.lua", "run");
    
        return 0;
    }
    

    测试脚本:

    -- test1.lua
    function init() output = 'test1' end
    function run() print(output) end
    
    -- test2.lua
    function init() output = 'test2' end
    function run() print(output) end
    

    输出:

    test1
    test2
    test2
    test1
    

    为简洁起见,我省略了所有错误处理,但您需要检查luaL_loadfile 的返回值并使用lua_pcall 而不是lua_call

    【讨论】:

      【解决方案2】:

      setfenv() 函数可用于为每个加载的文件创建一个sandbox 或环境。

      此示例显示所有三个文件都可以加载有冲突的函数,并且可以以任何顺序调用这些函数。类似的代码可以用 C++ 编写。此示例仅将打印功能导出到每个环境,您的场景可能需要更多。

      function newEnv()
        -- creates a simple environment
        return {["print"]=print}
      end
      
      local e={} -- environment table
      local c    -- chunk variable
      
      -- first instance
      c = loadstring([[function f() print("1") end]])
      e[#e+1] = newEnv()
      setfenv(c, e[#e]) -- set the loaded chunk's environment
      pcall(c) -- process the chunk (places the function into the enviroment)
      
      -- second instance
      c = loadstring([[function f() print("2") end]])
      e[#e+1] = newEnv()
      setfenv(c, e[#e])
      pcall(c)
      
      -- third instance
      c = loadstring([[function f() print("3") end]])
      e[#e+1] = newEnv()
      setfenv(c, e[#e])
      pcall(c)
      
      pcall(e[3].f) --> 3
      pcall(e[2].f) --> 2
      pcall(e[1].f) --> 1
      pcall(e[1].f) --> 1
      pcall(e[2].f) --> 2
      pcall(e[3].f) --> 3
      

      【讨论】:

      • 首先,谢谢!我在 lua 的 c++ 部分环顾四周,但找不到在 c++ 环境中哪个函数可以替换 lua 的 setfenv() 。这主要是因为我想在我的 c++ 代码中保留插件/插件控制(除了与 lua 相比更精通 c++)。假设我可以将环境创建合并到我的 Load_Plugins() 函数中,我是否正确?让 c++ 代码的其他部分可以使用环境列表?提前致谢
      • 更正发现 pgl.yoyo.org/luai/i/lua_setfenv 尽管 lua-users.org 列表表明该功能已被弃用,但我还是会尝试一下 :)
      • 弃用取决于您使用的 Lua 版本。 setfenv() 将在 5.2 中弃用。见corsix.org/content/look-lua-52-work3
      • 稍后再尝试,我终其一生都无法弄清楚上述 lua 代码如何转换为 c++ :(。我假设我没有正确使用 lua_setfenv 和 lua_getfenv因为应用程序只是崩溃了。
      【解决方案3】:

      您可以为每个文件创建一个新状态lua_newstate()。这将比我之前的答案更容易。但是,它可能会降低性能。

      【讨论】:

      • 好吧,如果为每个 lua 文件只创建一次新状态不会造成太大的性能损失,那么它应该不是问题。对状态的次要和后续访问不应该经常发生。我想我会尝试 newstate 的东西,因为我仍然无法让 lua_setfenv 在不使应用程序崩溃的情况下工作:(再次感谢 :)
      猜你喜欢
      • 1970-01-01
      • 2012-06-30
      • 2014-09-01
      • 2015-02-12
      • 2015-03-08
      • 1970-01-01
      • 2011-06-19
      • 2013-02-19
      相关资源
      最近更新 更多