Skip to content

Instantly share code, notes, and snippets.

@ThePhD
Last active August 29, 2015 14:00
Show Gist options
  • Save ThePhD/11304548 to your computer and use it in GitHub Desktop.
Save ThePhD/11304548 to your computer and use it in GitHub Desktop.
lua_class.c++ final
#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