From 2243bec0523cfd8b766691f0384816c640b67215 Mon Sep 17 00:00:00 2001 From: ThePhD Date: Fri, 25 Apr 2014 19:41:03 -0400 Subject: [PATCH] userdata now works and compiles on MSVC. It's going to take serious work to make it happen in GCC, plus the fact that 4.9 is still giving me so many errors I can't even read it... I'll let Rapptz figure it out, but later. Sexy class bindings, yes! --- main.cpp | 118 ------------------------------- sol/demangle.hpp | 56 +++++++-------- sol/lua_function.hpp | 93 +++++++------------------ sol/reference.hpp | 5 +- sol/stack.hpp | 21 ++++++ sol/state.hpp | 12 +++- sol/table.hpp | 50 ++++++++++++-- sol/traits.hpp | 6 +- sol/tuple.hpp | 4 ++ sol/types.hpp | 23 +++++++ sol/userdata.hpp | 161 +++++++++++++++++++++++++++++++++++++++++++ tests.cpp | 48 +++++++++++++ 12 files changed, 371 insertions(+), 226 deletions(-) delete mode 100644 main.cpp create mode 100644 sol/userdata.hpp diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 0532dee1..00000000 --- a/main.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include - -namespace sol { - - template - struct constructors; - - template - class lua_class { - public: - static const std::string classname; - static const std::string meta; - - private: - std::string luaname; - std::vector functionnames; - std::vector functions; - std::vector functiontable; - std::vector 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_newtable( L ); // 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( userdata ); - std::allocator alloc{ }; - alloc.construct( obj ); - } - - template - static int destruct( lua_State* L ) { - void* userdata = lua_touserdata( L, 0 ); - T* obj = static_cast( userdata ); - std::allocator alloc{ }; - alloc.destroy( obj ); - } - }; - - void build_function_tables( ) { - - } - - template - void build_function_tables( Ret( T::* func )( MArgs... ), std::string name, Args&&... args ) { - functionnames.push_back( std::move( name ) ); - functions.emplace_back( std::move( func ) ); - functiontable.push_back( { functionnames.back().c_str(), &lua_func::call } ); - build_function_tables( std::forward( args )... ); - } - - public: - template - lua_class( Args&&... args ) : lua_class( classname, std::forward( args )... ) { - - } - - template - lua_class( std::string name, Args&&... args ) : lua_class( name, constructors<>(), std::forward( args )... ) { - - } - - template - lua_class( std::string name, constructors c, Args&&... args ) : luaname( std::move( name ) ) { - functionnames.reserve( sizeof...( args ) ); - functiontable.reserve( sizeof...( args ) ); - metatable.reserve( sizeof...( args ) ); - build_function_tables( std::forward( args )... ); - functiontable. - } - }; - - template - const std::string lua_class::classname = detail::demangle( typeid( T ) ); - - template - const std::string lua_class::meta = std::string( "sol.stateful." ).append( classname ); - -} - -struct f { - int x; - f( ) : x( 1 ) { - } - f( int x ) : x( x ) { - } - int add( int y ) { - return x + y; - } -}; - -#include - -int main( ) { - sol::state s; - f x( 20 ); - - sol::lua_class lc{ }; - std::cout << lc.classname << std::endl; - std::cout << lc.meta << std::endl; - - s.set_function( "add", &f::add, f(10) ); - s.script( "t = add(20)" ); - std::cout << s.get( "t" ); -} diff --git a/sol/demangle.hpp b/sol/demangle.hpp index f9d0b1b2..3d1d11ba 100644 --- a/sol/demangle.hpp +++ b/sol/demangle.hpp @@ -32,27 +32,27 @@ namespace sol { namespace detail { - std::string demangle(const std::type_info& id) { - /*std::string realname(2048, '\0'); - DWORD result = UnDecorateSymbolName(id.raw_name(), &realname[0], - static_cast(realname.size()), UNDNAME_NAME_ONLY | UNDNAME_32_BIT_DECODE); - if (result == 0) - return ""; - realname.resize(result); - */ - const static std::string removals[ 2 ] = { - "struct ", - "class " - }; - std::string realname = id.name( ); - for ( std::size_t r = 0; r < 2; ++r ) { - auto found = realname.find( removals[ r ] ); - if ( found == std::string::npos ) - continue; - realname.erase( found, removals[r].size() ); - } - return realname; - } + std::string demangle(const std::type_info& id) { + /*std::string realname(2048, '\0'); + DWORD result = UnDecorateSymbolName(id.raw_name(), &realname[0], + static_cast(realname.size()), UNDNAME_NAME_ONLY | UNDNAME_32_BIT_DECODE); + if (result == 0) + return ""; + realname.resize(result); + */ + const static std::string removals[ 2 ] = { + "struct ", + "class " + }; + std::string realname = id.name(); + for (std::size_t r = 0; r < 2; ++r) { + auto found = realname.find(removals[ r ]); + if (found == std::string::npos) + continue; + realname.erase(found, removals[r].size()); + } + return realname; + } #elif __GNUC__ || __clang__ #include @@ -60,13 +60,13 @@ namespace detail { namespace sol { namespace detail { - std::string demangle(const std::type_info& id) { - int status; - char* unmangled = abi::__cxa_demangle(id.name(), 0, 0, &status); - std::string realname = unmangled; - free(unmangled); - return realname; - } + std::string demangle(const std::type_info& id) { + int status; + char* unmangled = abi::__cxa_demangle(id.name(), 0, 0, &status); + std::string realname = unmangled; + free(unmangled); + return realname; + } #else namespace sol { diff --git a/sol/lua_function.hpp b/sol/lua_function.hpp index 5110e0e3..a469348a 100644 --- a/sol/lua_function.hpp +++ b/sol/lua_function.hpp @@ -26,37 +26,6 @@ #include namespace sol { -namespace detail { -template -void get_upvalue_ptr(lua_State* L, T*& data, std::size_t datasize, std::array voiddata, int& upvalue) { - for (std::size_t i = 0, d = 0; d < datasize; ++i, d += sizeof(void*)) { - voiddata[i] = lua_touserdata(L, lua_upvalueindex(upvalue++)); - } - data = reinterpret_cast(static_cast(voiddata.data())); -} -template -void get_upvalue_ptr(lua_State* L, T*& data, std::array voiddata, int& upvalue) { - get_upvalue_ptr(L, data, sizeof(T), voiddata, upvalue); -} -template -void get_upvalue_ptr(lua_State* L, T*& data, int& upvalue) { - const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*); - typedef std::array data_t; - data_t voiddata{{}}; - return get_upvalue_ptr(L, data, voiddata, upvalue); -} -template -void get_upvalue(lua_State* L, T& data, int& upvalue) { - const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*); - typedef std::array data_t; - data_t voiddata{{}}; - for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { - voiddata[i] = lua_touserdata(L, lua_upvalueindex(upvalue++)); - } - data = *reinterpret_cast(static_cast(voiddata.data())); -} -} // detail - template struct static_lua_func { @@ -83,10 +52,9 @@ struct static_lua_func { } static int call(lua_State* L) { - int upvalue = 1; - fx_t* fx; - detail::get_upvalue(L, fx, upvalue); - int r = typed_call(tuple_types(), typename fx_traits::args_type(), fx, L); + auto udata = stack::get_user(L); + fx_t* fx = udata.first; + int r = typed_call(tuple_types(), typename fx_traits::args_type(), fx, L); return r; } @@ -122,19 +90,11 @@ struct static_object_lua_func { } 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; - int upvalue = 1; - data_t data = {{}}; - fx_t* fxptr; - for (std::size_t i = 0, d = 0; d < sizeof(fx_t*); ++i, d += sizeof(void*)) { - data[i] = lua_touserdata(L, lua_upvalueindex(upvalue++)); - } - fxptr = reinterpret_cast(static_cast(data.data())); - fx_t& mem_ptr = *fxptr; - void* objectdata = lua_touserdata(L, lua_upvalueindex(upvalue++)); - T& obj = *static_cast(objectdata); - int r = typed_call(tuple_types(), typename fx_traits::args_type(), obj, mem_ptr, L); + auto memberdata = stack::get_user(L, 1); + auto objdata = stack::get_user(L, memberdata.second); + fx_t& memfx = memberdata.first; + T& obj = *objdata.first; + int r = typed_call(tuple_types(), typename fx_traits::args_type(), obj, memfx, L); return r; } @@ -145,7 +105,7 @@ struct static_object_lua_func { struct lua_func { static int call(lua_State* L) { - void** pinheritancedata = static_cast(lua_touserdata(L, lua_upvalueindex(1))); + void** pinheritancedata = static_cast(stack::get(L, 1).value); void* inheritancedata = *pinheritancedata; if (inheritancedata == nullptr) throw sol_error("call from Lua to C++ function has null data"); @@ -156,7 +116,7 @@ struct lua_func { } static int gc(lua_State* L) { - void** puserdata = static_cast(lua_touserdata(L, 1)); + void** puserdata = static_cast(stack::get(L, 1).value); void* userdata = *puserdata; lua_func* ptr = static_cast(userdata); std::default_delete dx{}; @@ -175,15 +135,11 @@ template struct functor_lua_func : public lua_func { typedef decltype(&TFx::operator()) fx_t; typedef function_traits fx_traits; - fx_t fx; + TFx fx; template functor_lua_func(FxArgs&&... fxargs): fx(std::forward(fxargs)...) {} - virtual int operator()(lua_State* L) override { - return (*this)(tuple_types(), typename fx_traits::args_type(), L); - } - template int operator()(types, types t, lua_State* L) { stack::pop_call(L, fx, t); @@ -202,6 +158,10 @@ struct functor_lua_func : public lua_func { stack::push(L, r); return sizeof...(Ret); } + + virtual int operator()(lua_State* L) override { + return (*this)(tuple_types(), typename fx_traits::args_type(), L); + } }; template::value> @@ -286,21 +246,21 @@ struct class_lua_func : public lua_func { typedef typename std::remove_pointer::type>::type fx_t; typedef function_traits fx_traits; struct functor { - T& member; + T* item; fx_t invocation; template - functor(FxArgs&&... fxargs): member(*static_cast(nullptr)), invocation(std::forward(fxargs)...) {} + functor(FxArgs&&... fxargs): item(nullptr), invocation(std::forward(fxargs)...) {} - void pre_call( lua_State* L ) { - void* userdata = lua_touserdata( L, 0 ); - T* item = static_cast( userdata ); - member = *item; - } + void pre_call(lua_State* L) { + void* userdata = lua_touserdata(L, 0); + item = static_cast(userdata); + } template typename fx_traits::return_type operator()(Args&&... args) { - return (member.*invocation)(std::forward(args)...); + T& member = *item; + return (member.*invocation)(std::forward(args)...); } } fx; @@ -309,8 +269,7 @@ struct class_lua_func : public lua_func { template int operator()(types, types t, lua_State* L) { - fx.pre_call(L); - stack::pop_call(L, fx, t); + stack::pop_call(L, fx, t); return 0; } @@ -322,13 +281,13 @@ struct class_lua_func : public lua_func { template int operator()(types, types t, lua_State* L) { typedef typename multi_return::type return_type; - fx.pre_call(L); - return_type r = stack::pop_call(L, fx, t); + return_type r = stack::pop_call(L, fx, t); stack::push(L, std::move(r)); return sizeof...(Ret); } virtual int operator()(lua_State* L) override { + fx.pre_call(L); return (*this)(tuple_types(), typename fx_traits::args_type(), L); } }; diff --git a/sol/reference.hpp b/sol/reference.hpp index 90a2d2e1..b6808960 100644 --- a/sol/reference.hpp +++ b/sol/reference.hpp @@ -46,11 +46,14 @@ public: luaL_unref(L, LUA_REGISTRYINDEX, ref); } - void push() const noexcept { lua_rawgeti(L, LUA_REGISTRYINDEX, ref); } + void pop() const noexcept { + lua_pop(L, 1); + } + reference(reference&& o) noexcept { L = o.L; ref = o.ref; diff --git a/sol/stack.hpp b/sol/stack.hpp index e89a1c92..c948dbbd 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -112,6 +112,16 @@ inline bool get(lua_State* L, int index) { return lua_toboolean(L, index) != 0; } +template<> +inline lightuserdata_t get(lua_State* L, int index) { + return {lua_touserdata(L, lua_upvalueindex(index))}; +} + +template<> +inline userdata_t get(lua_State* L, int index) { + return {lua_touserdata(L, index)}; +} + template<> inline std::string get(lua_State* L, int index) { std::string::size_type len; @@ -124,6 +134,17 @@ inline const char* get(lua_State* L, int index) { return lua_tostring(L, index); } +template +inline std::pair get_user(lua_State* L, int index = 1) { + const static std::size_t data_t_count = (sizeof(T)+(sizeof(void*)-1)) / sizeof(void*); + typedef std::array data_t; + data_t voiddata{ {} }; + for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) { + voiddata[ i ] = stack::get(L, index++); + } + return std::pair(*reinterpret_cast(static_cast(voiddata.data())), index); +} + template inline T pop(lua_State* L) { auto r = get(L); diff --git a/sol/state.hpp b/sol/state.hpp index dea18762..80a27a79 100644 --- a/sol/state.hpp +++ b/sol/state.hpp @@ -144,9 +144,15 @@ public: return *this; } - template - state& set_function(T&& key, TFx&& fx, TM& mem) { - global.set_function(std::forward(key), std::forward(fx), mem); + template + state& set_function(T&& key, TFx&& fx, TObj&& obj) { + global.set_function(std::forward(key), std::forward(fx), std::forward(obj)); + return *this; + } + + template + state& set_class(userdata& user) { + global.set_class(user); return *this; } diff --git a/sol/table.hpp b/sol/table.hpp index 1688a58e..170e6817 100644 --- a/sol/table.hpp +++ b/sol/table.hpp @@ -25,8 +25,7 @@ #include "proxy.hpp" #include "stack.hpp" #include "lua_function.hpp" -#include -#include +#include "userdata.hpp" namespace sol { namespace detail { @@ -114,6 +113,46 @@ public: std::forward(key), std::forward(fx), std::forward(obj)); } + template + table& set_class(userdata& user) { + push(); + + lua_createtable(state(), 0, 0); + int classid = lua_gettop(state()); + + // Register metatable for user data in registry + // using the metaname key generated from the demangled name + luaL_newmetatable(state(), user.meta.c_str()); + int metaid = lua_gettop(state()); + // Meta functions: have no light up values + luaL_setfuncs(state(), user.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(state(), 0, user.functiontable.size() - 1); + for (std::size_t upvalues = 0; upvalues < user.functions.size(); ++upvalues) { + stack::push(state(), static_cast(user.functions[ upvalues ].get())); + } + luaL_setfuncs(state(), user.functiontable.data(), static_cast(user.functions.size())); + lua_setfield(state(), metaid, "__index"); + + // Meta functions: no upvalues + lua_createtable(state(), 0, user.metatable.size() - 1); + luaL_setfuncs(state(), user.metatable.data(), 0); // 0, for no upvalues + lua_setfield(state(), metaid, "__metatable"); + + lua_setmetatable(state(), classid); + + lua_setglobal(state(), user.luaname.c_str()); + + pop(); + + return *this; + } + size_t size() const { push(); return lua_rawlen(state(), -1); @@ -194,7 +233,7 @@ private: stack::push(state(), userobjdata); luaL_setfuncs(state(), funcreg, upvalues + 1); - lua_pop(state(), 1); + pop(); return *this; } @@ -210,11 +249,10 @@ private: }; push(); - int upvalues = stack::push_user(state(), target); luaL_setfuncs(state(), funcreg, upvalues); + pop(); - lua_pop(state(), 1); return *this; } @@ -243,7 +281,7 @@ private: push(); stack::push_user(state(), userdata, metatablename); luaL_setfuncs(state(), funcreg, 1); - lua_pop(state(), 1); + pop(); return *this; } }; diff --git a/sol/traits.hpp b/sol/traits.hpp index cb082ca7..08b6f36b 100644 --- a/sol/traits.hpp +++ b/sol/traits.hpp @@ -58,7 +58,7 @@ using Bool = std::integral_constant; namespace detail { template>::value> -struct is_function_impl : std::is_function::type> { }; +struct is_function_impl : std::is_function::type> {}; template struct is_function_impl { @@ -66,7 +66,7 @@ struct is_function_impl { using no = struct { char s[2]; }; struct F { void operator()(); }; - struct Derived : T, F { }; + struct Derived : T, F {}; template struct Check; template @@ -80,7 +80,7 @@ struct is_function_impl { } // detail template -struct Function : Bool::value> { }; +struct Function : Bool::value> {}; template struct function_traits; diff --git a/sol/tuple.hpp b/sol/tuple.hpp index cecaa3e8..8b974b3f 100644 --- a/sol/tuple.hpp +++ b/sol/tuple.hpp @@ -73,6 +73,10 @@ struct tuple_types : types, std::false_type {}; template struct tuple_types> : types, std::true_type {}; + +template +struct constructors {}; + } // sol #endif // SOL_TUPLE_HPP \ No newline at end of file diff --git a/sol/types.hpp b/sol/types.hpp index e84682e0..c4284d3e 100644 --- a/sol/types.hpp +++ b/sol/types.hpp @@ -31,6 +31,17 @@ struct nil_t {}; const nil_t nil {}; struct void_type {}; const void_type Void {}; +struct lightuserdata_t { + void* value; + lightuserdata_t(void* data) : value(data) {} + operator void* () const { return value; } +}; + +struct userdata_t { + void* value; + userdata_t(void* data) : value(data) {} + operator void* () const { return value; } +}; enum class type : int { none = LUA_TNONE, @@ -58,6 +69,8 @@ inline void type_assert(lua_State* L, int index, type expected) { } } +template +class userdata; class table; class function; class object; @@ -114,6 +127,16 @@ inline type type_of() { return type::boolean; } +template<> +inline type type_of() { + return type::lightuserdata; +} + +template<> +inline type type_of() { + return type::userdata; +} + inline bool operator==(nil_t, nil_t) { return true; } inline bool operator!=(nil_t, nil_t) { return false; } } // sol diff --git a/sol/userdata.hpp b/sol/userdata.hpp new file mode 100644 index 00000000..817dc521 --- /dev/null +++ b/sol/userdata.hpp @@ -0,0 +1,161 @@ +// The MIT License (MIT) + +// Copyright (c) 2013 Danny Y., Rapptz + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#ifndef SOL_USERDATA_HPP +#define SOL_USERDATA_HPP + +#include +#include +#include +#include + +namespace sol { + +template +class userdata { +private: + friend table; + static const std::string classname; + static const std::string meta; + + std::string luaname; + std::vector functionnames; + std::vector> functions; + std::vector functiontable; + std::vector metatable; + + struct constructor { + 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(userdata); + std::allocator alloc{}; + alloc.construct(obj); + + luaL_getmetatable(L, meta.c_str()); + lua_setmetatable(L, -2); + lua_setfield(L, -2, "__self"); + + return 1; + } + }; + + template + struct destructor { + static int destruct(lua_State* L) { + for (std::size_t i = 0; i < n; ++i) { + lightuserdata_t luserdata = stack::get(L, i); + } + userdata_t userdata = stack::get(L, 0); + T* obj = static_cast(userdata.value); + std::allocator alloc{}; + alloc.destroy(obj); + + return 0; + } + }; + + template + struct class_func { + static int call(lua_State* L) { + // Zero-based template parameter, but upvalues start at 1 + void* inheritancedata = stack::get(L, i + 1); + if (inheritancedata == nullptr) + throw sol_error("call from Lua to C++ function has null data"); + lua_func* pfx = static_cast(inheritancedata); + lua_func& fx = *pfx; + int r = fx(L); + return r; + } + }; + + template + void build_function_tables() { + + } + + template + void build_function_tables(Ret(T::* func)(MArgs...), std::string name, Args&&... args) { + typedef typename std::decay::type fx_t; + functionnames.push_back(std::move(name)); + functions.emplace_back(std::make_unique>(std::move(func))); + functiontable.push_back({ functionnames.back().c_str(), &class_func::call }); + build_function_tables(std::forward(args)...); + } + +public: + template + userdata(Args&&... args) : userdata(classname, std::forward(args)...) { + + } + + template + userdata(std::string name, Args&&... args) : userdata(name, constructors<>(), std::forward(args)...) { + + } + + template + userdata(constructors c, Args&&... args) : userdata(classname, std::move(c), std::forward(args)...) { + + } + + template + userdata(std::string name, constructors 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)...); + + functionnames.push_back("new"); + functiontable.push_back({ functionnames.back().c_str(), &constructor::construct }); + functiontable.push_back({ nullptr, nullptr }); + + metatable.push_back({ "__gc", &destructor::destruct }); + metatable.push_back({ nullptr, nullptr }); + } + + void register_into(const table& s) { + + } +}; + +template +const std::string userdata::classname = detail::demangle(typeid(T)); + +template +const std::string userdata::meta = std::string("sol.stateful.").append(classname); + +} + +#endif SOL_USERDATA_HPP diff --git a/tests.cpp b/tests.cpp index c4e546a7..c916a79d 100644 --- a/tests.cpp +++ b/tests.cpp @@ -16,6 +16,26 @@ struct object { }; +struct fuser { + int x; + + fuser( ) : x( 0 ) { + + } + + fuser( int x ) : x( x ) { + + } + + int add( int y ) { + return x + y; + } + + int add2( int y ) { + return x + y + 2; + } +}; + int plop_xyz(int x, int y, std::string z) { std::cout << x << " " << y << " " << z << std::endl; return 11; @@ -324,3 +344,31 @@ TEST_CASE("tables/operator[]", "Check if operator[] retrieval and setting works REQUIRE_NOTHROW(assert1(lua.global_table())); } + +TEST_CASE( "tables/userdata", "Show that we can create classes from userdata and use them" ) { + + sol::state lua; + + sol::userdata lc{ &fuser::add, "add", &fuser::add2, "add2" }; + lua.set_class( lc ); + + lua.script( "a = fuser:new()\n" + "b = a:add(1)\n" + "c = a:add2(1)\n" + "\n" ); + + sol::object a = lua.get( "a" ); + sol::object b = lua.get( "b" ); + sol::object c = lua.get( "c" ); + REQUIRE( ( a.is( ) ) ); + auto atype = a.get_type( ); + auto btype = b.get_type( ); + auto ctype = c.get_type( ); + REQUIRE( ( atype == sol::type::table ) ); + REQUIRE( ( btype == sol::type::number ) ); + REQUIRE( ( ctype == sol::type::number ) ); + int bresult = b.as( ); + int cresult = c.as( ); + REQUIRE( bresult == 1 ); + REQUIRE( cresult == 3 ); +} \ No newline at end of file