diff --git a/sol/functional.hpp b/sol/functional.hpp index 8618bdbc..708cad24 100644 --- a/sol/functional.hpp +++ b/sol/functional.hpp @@ -38,6 +38,7 @@ struct function_traits { typedef types args_type; typedef R(T::* function_pointer_type)(Args...); typedef typename std::remove_pointer::type function_type; + typedef R(* free_function_pointer_type)(Args...); typedef R return_type; template using arg = typename std::tuple_element::type; @@ -49,8 +50,9 @@ struct function_traits { static const bool is_member_function = true; typedef std::tuple arg_tuple_type; typedef types args_type; - typedef R(T::* function_type)(Args...); typedef R(T::* function_pointer_type)(Args...); + typedef typename std::remove_pointer::type function_type; + typedef R(* free_function_pointer_type)(Args...); typedef R return_type; template using arg = typename std::tuple_element::type; @@ -64,6 +66,7 @@ struct function_traits { typedef types args_type; typedef R(function_type)(Args...); typedef R(*function_pointer_type)(Args...); + typedef R(* free_function_pointer_type)(Args...); typedef R return_type; template using arg = typename std::tuple_element::type; @@ -77,6 +80,7 @@ struct function_traits { typedef types args_type; typedef R(function_type)(Args...); typedef R(*function_pointer_type)(Args...); + typedef R(* free_function_pointer_type)(Args...); typedef R return_type; template using arg = typename std::tuple_element::type; diff --git a/sol/lua_function.hpp b/sol/lua_function.hpp index 99d54439..44e41cac 100644 --- a/sol/lua_function.hpp +++ b/sol/lua_function.hpp @@ -26,10 +26,101 @@ #include "stack.hpp" namespace sol { -namespace detail { + +template +struct static_lua_func { + typedef typename std::remove_pointer::type>::type fx_t; + typedef detail::function_traits fx_traits; + + template + static int typed_call( types, types t, fx_t* fx, lua_State* L ) { + stack::pop_call( L, fx, t ); + return 0; + } + + template + static int typed_call( types, types t, fx_t* fx, lua_State* L ) { + auto r = stack::pop_call( L, fx, t ); + stack::push( L, std::move( r ) ); + return sizeof...( TRn ); + } + + static int call( lua_State* L ) { + void* functiondata = lua_touserdata( L, lua_upvalueindex( 1 ) ); + //if ( functiondata == nullptr ) + // throw sol_error( "call from lua to c++ function has null function pointer data" ); + fx_t* fx = *static_cast( functiondata ); + int r = typed_call( tuple_types( ), typename fx_traits::args_type( ), fx, L ); + return r; + } + + int operator()( lua_State* L ) { + return call( L ); + } +}; + +template +struct static_object_lua_func { + typedef typename std::decay::type fx_t; + typedef detail::function_traits fx_traits; + + template + static int typed_call( types, types, T& item, fx_t& ifx, lua_State* L ) { + auto fx = [ &item, &ifx ] ( Args&&... args ) { ( item.*ifx )( std::forward( args )... ); }; + stack::pop_call( L, fx, types( ) ); + return 0; + } + + template + static int typed_call( types, types, T& item, fx_t& ifx, lua_State* L ) { + auto fx = [ &item, &ifx ] ( Args&&... args ) -> TR { return ( item.*ifx )( std::forward( args )... ); }; + auto r = stack::pop_call( L, fx, types( ) ); + stack::push( L, std::move( r ) ); + return 1; + } + + template + static int typed_call( types, types, T& item, fx_t& ifx, lua_State* L ) { + auto fx = [ &item, &ifx ] ( Args&&... args ) -> std::tuple { return ( item.*ifx )( std::forward( args )... ); }; + auto r = stack::pop_call( L, fx, types( ) ); + stack::push( L, std::move( r ) ); + return sizeof...( TRn ); + } + + static int call( lua_State* L ) { + const static std::size_t data_t_count = ( sizeof(fx_t)+( sizeof(void*)-1 ) ) / sizeof( void* ); + typedef std::array data_t; + data_t fxptrdata; + int upvalue = 1; + for ( std::size_t i = 0; i < fxptrdata.size( ); ++i ) { + fxptrdata[ i ] = lua_touserdata( L, lua_upvalueindex( upvalue++ ) ); + } + void* objectdata = lua_touserdata( L, lua_upvalueindex( upvalue++ ) ); + //if ( objectdata == nullptr ) + // throw sol_error( "call from lua to c++ function has null object data" ); + fx_t* fxptr = reinterpret_cast( static_cast( fxptrdata.data( ) ) ); + fx_t& mem_ptr = *fxptr; + T& obj = *static_cast( objectdata ); + int r = typed_call( tuple_types( ), typename fx_traits::args_type( ), obj, mem_ptr, L ); + return r; + } + + int operator()( lua_State* L ) { + return call( L ); + } +}; struct lua_func { - virtual int operator()(lua_State*) { + static int call( lua_State* L ) { + void* inheritancedata = lua_touserdata( L, lua_upvalueindex( 1 ) ); + if ( inheritancedata == nullptr ) + throw sol_error( "call from lua to c++ function has null data" ); + lua_func& fx = *static_cast( inheritancedata ); + int r = fx( L ); + return r; + } + + virtual int operator()(lua_State*) { throw sol_error("Failure to call specialized wrapped C++ function from lua"); } @@ -39,7 +130,7 @@ struct lua_func { template struct lambda_lua_func : public lua_func { typedef decltype(&TFx::operator()) fx_t; - typedef function_traits fx_traits; + typedef detail::function_traits fx_traits; TFx fx; template @@ -66,7 +157,7 @@ struct lambda_lua_func : public lua_func { template::value> struct explicit_lua_func : public lua_func { typedef typename std::remove_pointer::type>::type fx_t; - typedef function_traits fx_traits; + typedef detail::function_traits fx_traits; TFx fx; template @@ -93,7 +184,7 @@ struct explicit_lua_func : public lua_func { template struct explicit_lua_func : public lua_func { typedef typename std::remove_pointer::type>::type fx_t; - typedef function_traits fx_traits; + typedef detail::function_traits fx_traits; struct lambda { T* member; TFx invocation; @@ -131,14 +222,6 @@ struct explicit_lua_func : public lua_func { } }; - -int lua_cfun(lua_State* L) { - void* bridgedata = lua_touserdata(L, lua_upvalueindex(1)); - auto* fx = static_cast(bridgedata); - int r = fx->operator()(L); - return r; -} -} // detail } // sol #endif // SOL_LUA_FUNC_HPP diff --git a/sol/stack.hpp b/sol/stack.hpp index 08a79895..5a0fce90 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -26,6 +26,7 @@ #include "tuple.hpp" #include #include +#include namespace sol { template @@ -156,10 +157,21 @@ inline void push(lua_State* L, const char (&str)[N]) { lua_pushlstring(L, str, N - 1); } +inline void push(lua_State* L, const char* str) { + lua_pushlstring(L, str, std::char_traits::length( str )); +} + inline void push(lua_State* L, const std::string& str) { lua_pushlstring(L, str.c_str(), str.size()); } +template +inline void push( lua_State* L, const std::array& data ) { + for ( std::size_t i = 0; i < data.size( ); ++i ) { + push( L, data[ i ] ); + } +} + namespace detail { template inline void push(lua_State* L, indices, const T& tuplen) { diff --git a/sol/table.hpp b/sol/table.hpp index 8d434fcb..13f8f6d3 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -25,12 +25,25 @@ #include "stack.hpp" #include "lua_function.hpp" #include +#include #include + namespace sol { +namespace detail { + template + T* get_ptr( T& val ) { + return std::addressof( val ); + } + + template + T* get_ptr( T* val ) { + return val; + } +} class table : virtual public reference { private: - std::unordered_map> funcs; + std::unordered_map> funcs; public: table() noexcept: reference(), funcs() {} table(lua_State* L, int index = -1): reference(L, index), funcs() { @@ -61,15 +74,13 @@ public: template table& set_function(T&& key, TFx&& fx) { typedef typename std::remove_pointer::type>::type clean_fx; - const static bool isfunction = std::is_function::value; - return set_fx(std::integral_constant(), std::forward(key), std::forward(fx)); + return set_isfunction_fx( std::is_function( ), std::forward( key ), std::forward( fx ) ); } - template - table& set_function(T&& key, TFx&& fx, TM& mem) { - typedef typename std::remove_pointer::type>::type clean_fx; - std::unique_ptr sptr(new detail::explicit_lua_func(mem, std::forward(fx))); - return set_fx(std::forward(key), std::move(sptr)); + template + table& set_function(T&& key, TFx&& fx, TObj&& obj) { + return set_lvalue_fx( std::integral_constant::value || std::is_pointer::value>( ), + std::forward( key ), std::forward( fx ), std::forward( obj ) ); } size_t size() const { @@ -77,54 +88,133 @@ public: return lua_rawlen(state(), -1); } private: + + template + table& set_isfunction_fx( std::true_type, T&& key, TFx&& fx ) { + return set_fx( std::false_type( ), std::forward( key ), std::forward( fx ) ); + } + template - table& set_fx(std::true_type, T&& key, TFx&& fx) { - typedef typename std::remove_pointer::type>::type clean_fx; - std::unique_ptr sptr(new detail::lambda_lua_func(std::forward(fx))); - return set_fx(std::forward(key), std::move(sptr)); + table& set_isfunction_fx(std::false_type, T&& key, TFx&& fx) { + typedef typename std::decay::type clean_lambda; + typedef typename detail::function_traits::free_function_pointer_type raw_func_t; + typedef std::is_convertible isconvertible; + return set_isconvertible_fx( isconvertible( ), std::forward( key ), std::forward( fx ) ); + } + + template + table& set_isconvertible_fx( std::true_type, T&& key, TFx&& fx ) { + typedef typename std::decay::type clean_lambda; + typedef typename detail::function_traits::free_function_pointer_type raw_func_t; + return set_isfunction_fx( std::true_type( ), std::forward( key ), raw_func_t( std::forward( fx ) ) ); + } + + template + table& set_isconvertible_fx( std::false_type, T&& key, TFx&& fx ) { + typedef typename std::remove_pointer::type>::type clean_fx; + std::unique_ptr sptr( new lambda_lua_func( std::forward( fx ) ) ); + return set_fx( std::forward( key ), std::move( sptr ) ); + } + + template + table& set_lvalue_fx( std::true_type, T&& key, TFx&& fx, TObj&& obj ) { + return set_fx( std::true_type(), std::forward( key ), std::forward( fx ), std::forward( obj ) ); + } + + template + table& set_lvalue_fx( std::false_type, T&& key, TFx&& fx, TM& mem ) { + typedef typename std::remove_pointer::type>::type clean_fx; + std::unique_ptr sptr( new explicit_lua_func( mem, std::forward( fx ) ) ); + return set_fx( std::forward( key ), std::move( sptr ) ); + } + + template + table& set_fx( std::true_type, T&& key, TFx&& fx, TObj&& obj ) { + typedef typename std::decay::type decay_of_to; + typedef typename std::decay::type decay_of_tfx; + const static std::size_t data_t_count = ( sizeof(decay_of_tfx)+( sizeof(void*)-1 ) ) / sizeof( void* ); + typedef std::array data_t; + + std::string fkey( key ); + + // Layout: + // idx 1...n: verbatim data of member function pointer + // idx n + 1: is the object's void pointer + // We don't need to store the size, because the other side is templated + // with the same member function pointer type + decay_of_tfx fxptr( std::forward( fx ) ); + data_t fxptrdata; + std::size_t fxptrsize = sizeof( fxptr ); + std::memcpy( std::addressof( fxptrdata[ 0 ] ), std::addressof( fxptr ), fxptrsize ); + void* userobjdata = static_cast( detail::get_ptr( obj ) ); + lua_CFunction freefunc = &static_object_lua_func::call; + const char* freefuncname = fkey.c_str( ); + const luaL_Reg funcreg[ 2 ] = { + { freefuncname, freefunc }, + { } + }; + + + push( ); + + stack::push( state( ), fxptrdata ); + stack::push( state( ), userobjdata ); + luaL_setfuncs( state( ), funcreg, fxptrdata.size() + 1 ); + + lua_pop( state( ), 1 ); + return *this; } template table& set_fx(std::false_type, T&& key, TFx&& fx) { typedef typename std::decay::type ptr_fx; - std::unique_ptr sptr(new detail::explicit_lua_func(std::forward(fx))); - return set_fx(std::forward(key), std::move(sptr)); - } - - template - table& set_fx(T&& key, std::unique_ptr luafunc) { - std::string fkey(key); - auto hint = funcs.find(fkey); - if (hint == funcs.end()) { - std::shared_ptr sptr(luafunc.release()); - hint = funcs.emplace_hint(hint, fkey, std::move(sptr)); - } - else { - hint->second.reset(luafunc.release()); - } - detail::lua_func* target = hint->second.get(); + std::string fkey( key ); + ptr_fx target( std::forward( fx ) ); void* userdata = static_cast( target ); - lua_CFunction freefunc = &detail::lua_cfun; - const char* freefuncname = hint->first.c_str( ); + lua_CFunction freefunc = &static_lua_func::call; + const char* freefuncname = fkey.c_str( ); const luaL_Reg funcreg[ 2 ] = { { freefuncname, freefunc }, { } }; push( ); - - /*//lua_pushlightuserdata( state(), userdata ); - lua_pushstring( state(), freefuncname ); // push key onto stack - lua_pushcclosure( state(), freefunc, 1 ); // push value onto stack - lua_settable( state(), -3 ); - */ - - lua_pushlightuserdata( state( ), userdata ); + + stack::push( state( ), target ); luaL_setfuncs( state( ), funcreg, 1 ); - + lua_pop( state( ), 1 ); return *this; } + + template + table& set_fx( T&& key, std::unique_ptr luafunc ) { + std::string fkey( key ); + auto hint = funcs.find( fkey ); + if ( hint == funcs.end( ) ) { + std::shared_ptr sptr( luafunc.release( ) ); + hint = funcs.emplace_hint( hint, fkey, std::move( sptr ) ); + } + else { + hint->second.reset( luafunc.release( ) ); + } + lua_func* target = hint->second.get( ); + void* userdata = static_cast( target ); + lua_CFunction freefunc = &lua_func::call; + const char* freefuncname = hint->first.c_str( ); + const luaL_Reg funcreg[ 2 ] = { + { freefuncname, freefunc }, + { } + }; + + push( ); + + stack::push( state( ), userdata ); + luaL_setfuncs( state( ), funcreg, 1 ); + + lua_pop( state( ), 1 ); + return *this; + } }; } // sol