Last active
August 29, 2015 14:00
-
-
Save ThePhD/11304548 to your computer and use it in GitHub Desktop.
lua_class.c++ final
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sol/state.hpp> | |
#include <sol/lua_function.hpp> | |
#include <sol/demangle.hpp> | |
#include <vector> | |
namespace sol { | |
template <typename... Tn> | |
struct constructors { }; | |
template <typename T> | |
class lua_class { | |
public: | |
static const std::string classname; | |
static const std::string meta; | |
private: | |
std::string luaname; | |
std::vector<std::string> functionnames; | |
std::vector<std::unique_ptr<lua_func>> functions; | |
std::vector<luaL_Reg> functiontable; | |
std::vector<luaL_Reg> metatable; | |
struct maker { | |
static int construct( lua_State* L ) { | |
// First argument is now a table that represent the class to instantiate | |
luaL_checktype( L, 1, LUA_TTABLE ); | |
lua_createtable( L, 0, 0 ); // Create table to represent instance | |
// Set first argument of new to metatable of instance | |
lua_pushvalue( L, 1 ); | |
lua_setmetatable( L, -2 ); | |
// Do function lookups in metatable | |
lua_pushvalue( L, 1 ); | |
lua_setfield( L, 1, "__index" ); | |
void* userdata = lua_newuserdata( L, sizeof( T ) ); | |
T* obj = static_cast<T*>( userdata ); | |
std::allocator<T> alloc{ }; | |
alloc.construct( obj ); | |
luaL_getmetatable( L, meta.c_str() ); | |
lua_setmetatable( L, -2 ); | |
lua_setfield( L, -2, "__self" ); | |
return 1; | |
} | |
}; | |
template <std::size_t n> | |
struct breaker { | |
static int destruct( lua_State* L ) { | |
for ( std::size_t i = 0; i < n; ++i ) { | |
light_user_data luserdata = stack::get<light_user_data>( L, i ); | |
} | |
user_data userdata = stack::get<user_data>( L, 0 ); | |
T* obj = static_cast<T*>( userdata.userdata ); | |
std::allocator<T> alloc{ }; | |
alloc.destroy( obj ); | |
return 0; | |
} | |
}; | |
template <std::size_t i> | |
struct class_func { | |
static int call( lua_State* L ) { | |
// Zero-based template parameter, but upvalues start at 1 | |
void* inheritancedata = stack::get<light_user_data>( L, i + 1 ); | |
if ( inheritancedata == nullptr ) | |
throw sol_error( "call from Lua to C++ function has null data" ); | |
lua_func* pfx = static_cast<lua_func*>( inheritancedata ); | |
lua_func& fx = *pfx; | |
int r = fx( L ); | |
return r; | |
} | |
}; | |
template <std::size_t n> | |
void build_function_tables( ) { | |
} | |
template <std::size_t n, typename... Args, typename Ret, typename... MArgs> | |
void build_function_tables( Ret( T::* func )( MArgs... ), std::string name, Args&&... args ) { | |
typedef typename std::decay<decltype( func )>::type fx_t; | |
functionnames.push_back( std::move( name ) ); | |
functions.emplace_back( std::make_unique<class_lua_func<fx_t, T>>( std::move( func ) ) ); | |
functiontable.push_back( { functionnames.back().c_str(), &class_func<n>::call } ); | |
build_function_tables<n + 1>( std::forward<Args>( args )... ); | |
} | |
public: | |
template <typename... Args> | |
lua_class( Args&&... args ) : lua_class( classname, std::forward<Args>( args )... ) { | |
} | |
template <typename... Args> | |
lua_class( std::string name, Args&&... args ) : lua_class( name, constructors<>( ), std::forward<Args>( args )... ) { | |
} | |
template <typename... Args, typename... CArgs> | |
lua_class( constructors<CArgs...> c, Args&&... args ) : lua_class( classname, std::move( c ), std::forward<Args>( args )... ) { | |
} | |
template <typename... Args, typename... CArgs> | |
lua_class( std::string name, constructors<CArgs...> c, Args&&... args ) : luaname( std::move( name ) ) { | |
functionnames.reserve( sizeof...( args ) ); | |
functiontable.reserve( sizeof...( args ) ); | |
functions.reserve( sizeof...( args ) ); | |
metatable.reserve( sizeof...( args ) ); | |
build_function_tables<0>( std::forward<Args>( args )... ); | |
functionnames.push_back( "new" ); | |
functiontable.push_back( { functionnames.back( ).c_str( ), &maker::construct } ); | |
functiontable.push_back( { nullptr, nullptr } ); | |
metatable.push_back( { "__gc", &breaker<sizeof...( Args ) / 2>::destruct } ); | |
metatable.push_back( { nullptr, nullptr } ); | |
} | |
void register_into ( const table& s ) { | |
table.push(); | |
lua_State* L = s.state( ); | |
lua_createtable( L, 0, 0 ); | |
int classid = lua_gettop( L ); | |
// Register metatable for user data in registry | |
// using the metaname key generated from the demangled name | |
luaL_newmetatable( L, meta.c_str( ) ); | |
int metaid = lua_gettop( L ); | |
// Meta functions: have no light up values | |
luaL_setfuncs( L, metatable.data(), 0 ); | |
// Regular functions: each one references an upvalue at its own index, | |
// resulting in [function count] upvalues | |
//luaL_newlib( L, functiontable.data( ) ); | |
// the newlib macro doesn't have a form for when you need upvalues: | |
// we duplicate the work below | |
lua_createtable( L, 0, functiontable.size( ) - 1 ); | |
for ( std::size_t upvalues = 0; upvalues < functions.size( ); ++upvalues ) { | |
stack::push( L, static_cast<void*>( functions[ upvalues ].get( ) ) ); | |
} | |
luaL_setfuncs( L, functiontable.data( ), static_cast<uint32_t>( functions.size( ) ) ); | |
lua_setfield( L, metaid, "__index" ); | |
// Meta functions: no upvalues | |
lua_createtable( L, 0, metatable.size( ) - 1 ); | |
luaL_setfuncs( L, metatable.data( ), 0 ); // 0, for no upvalues | |
lua_setfield( L, metaid, "__metatable" ); | |
lua_setmetatable( L, classid ); | |
lua_setglobal( L, luaname.c_str( ) ); | |
table.pop( ); | |
} | |
}; | |
template <typename T> | |
const std::string lua_class<T>::classname = detail::demangle( typeid( T ) ); | |
template <typename T> | |
const std::string lua_class<T>::meta = std::string( "sol.stateful." ).append( classname ); | |
} | |
#include <sol.hpp> | |
#include <iostream> | |
struct f { | |
int x; | |
f( ) : x( 1 ) { | |
} | |
f( int x ) : x( x ) { | |
} | |
int add( int y ) { | |
return x + y; | |
} | |
int add2( int y ) { | |
return x + y + 1; | |
} | |
}; | |
int main( ) { | |
sol::state s; | |
sol::lua_class<f> lc{ &f::add, "add" }; | |
std::cout << lc.classname << std::endl; | |
std::cout << lc.meta << std::endl; | |
lc.register_into( s ); | |
s.script( "a = f:new()\n" | |
"b = a:add(1)\n" | |
"\n" ); | |
sol::object a = s.get<sol::object>( "a" ); | |
sol::object b = s.get<sol::object>( "b" ); | |
bool aistable = a.is<sol::table>( ); | |
auto atype = a.get_type( ); | |
int bresult = b.as<int>( ); | |
std::cout << bresult << std::endl; | |
s.set_function( "add2", &f::add2, f( 10 ) ); | |
s.script( "t = add2(20)" ); | |
std::cout << s.get<int>( "t" ); | |
f x( 10 ); | |
s.set_function( "add2", &f::add2, x ); | |
s.script( "t = add2(20)" ); | |
std::cout << s.get<int>( "t" ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment