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