Rapptz / sol

A C++11 Lua wrapper

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Setting functions in tables doesn't work.

Rapptz opened this issue · comments

Can't set a function inside a table, user made or otherwise. Doing it through sol::state::script works just fine though.

Simple test case:

#include <sol.hpp>
#include <iostream>
#include <iomanip>

int main() {
    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::os);
    lua.get<sol::table>("os").set_function("fun", []() {
        return "test";
    });
    lua.get<sol::table>("os").set("name", "windows");
    lua.script("assert(os.fun() == \"test\")\n" // fails
               "assert(os.name == \"windows\")");
}

It's because of this line.

Can fix it with this:

   const char* freefuncname = hint->first.c_str( );
   const luaL_Reg funcreg[ 2 ] = {
       { freefuncname, freefunc },
       { }
   };

   push( );

   lua_pushlightuserdata( state( ), userdata );
   luaL_setfuncs( state( ), funcreg, 1 );

   lua_pop( state( ), 1 );

However, another underlying problem is that table is implicitly constructed from a lua table: thus, the funcs storage is not useful because it gets destructed. This results in an access violation because the data given to lua_pushlightuserdata is invalid (the shared_ptrs are destroyed) by the time the script is executed.

This is actually a non-issue for member and free functions: a little extra work and one or two extra light user datas means we can use a templated static function to get the right call out of these two types of functors.

Lambdas (and other potential state-holding structs that evaporate on their original stack if they're passed in by move or by value to the set_function) are a significant problem because you must store that lambda in order to preserve its state. This is why the detail::lua_func inheritance was created.

A solution is to elevate the funcs table to sol::state and let the sol::state do the tracking / keep-alive work. We can then pass that implicit to the table version of set_function, and all outsiders would have to provide some kind of function storage to table for it to work (if they don't go through sol::state to set the function).

It's kind of hacky and honestly will result in a little bit of boilerplate code. The next thing up is to have lua do allocation and deallocation of these things. This is a bit more dangerous, and I have no idea the limitations on lua's allocattion model. My gut tells me if we have enough large, stateful lambdas, we could cause an atpanic and ruin the beautiful illusion.

I'll try to explore either case.

Massive refactoring on sol::table to do aggressive optimizations to eliminate having to new up function storage opens up a lot of use cases that previously were unavailable to users. This partially solves the function storage issues. Any further fixes should be directed at these lines here, which is the one place where dynamic memory is given to lua to handle (which subsequently explodes because it's being stored on table). Working and unworking test cases.