Search code examples
c++lualua-api

Calling lua functions from .lua's using handles?


I'm working on a small project trying to integrate lua with c++. My problem however is as follows:

I have multiple lua scripts, lets call them s1.lua s2.lua and s3.lua. Each of these has the following functions: setVars() and executeResults().

Now I am able to to call a lua file through LuaL_dofile and immediately after use setVars() and/or executeResults(). The problem here however is that after I load s2.lua I can no longer call the functions of s1.lua. This would mean I have to redo the LuaL_dofile on s1.lua to regain access to the function and by doing so I lose access to the functions in s2.lua.

Is there a way to simply load all lua files in a row, and afterwards start calling their functions at will? Something like s1->executeResults() s5->executeResults() s3->setVars() etc.

I currently already have a system in place using boost::filesystem to detect all lua files in a folder, I then save these files names in a vector and then simply iterate over the vector to load each lua file in a row.

Foregoing the filling of the vector with lua file names my plugin load function looks like this at the moment:

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;
  }
 }
}

To make it a bit more clear, all I have in the .lua's is something like this:

-- s1.lua

setVars()
--do stuff
end

executeResults()
--dostuff
end

etc, but I would like to be able to call s1.lua's setVars() and s2.lua's setVars() after simply having loaded both in a row.


Solution

  • This is effectively what gwell proposed using the 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;
    }
    

    Test scripts:

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

    Output:

    test1
    test2
    test2
    test1
    

    I omitted all error handling for brevity, but you'll want to check the return value of luaL_loadfile and use lua_pcall instead of lua_call.