// Copyright satoren // Distributed under the Boost Software License, Version 1.0. (See // accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include "kaguya/config.hpp" #include "kaguya/utility.hpp" #include "kaguya/traits.hpp" #include "kaguya/exception.hpp" namespace kaguya { namespace types { template struct typetag {}; } inline void *metatable_name_key() { static int key; return &key; } inline void *metatable_type_table_key() { static int key; return &key; } template const std::type_info &metatableType() { return typeid(typename traits::decay::type); } template inline std::string metatableName() { return util::pretty_name(metatableType()); } struct ObjectWrapperBase { virtual const void *cget() = 0; virtual void *get() = 0; virtual const std::type_info &type() = 0; virtual const std::type_info &native_type() { return type(); } virtual void *native_get() { return get(); } ObjectWrapperBase() {} virtual ~ObjectWrapperBase() {} private: // noncopyable ObjectWrapperBase(const ObjectWrapperBase &); ObjectWrapperBase &operator=(const ObjectWrapperBase &); }; template struct ObjectWrapper : ObjectWrapperBase { #if KAGUYA_USE_CPP11 template ObjectWrapper(Args &&... args) : object_(std::forward(args)...) {} #else ObjectWrapper() : object_() {} #define KAGUYA_OBJECT_WRAPPER_CONSTRUCTOR_DEF(N) \ template \ ObjectWrapper(KAGUYA_PP_ARG_DEF_REPEAT(N)) \ : object_(KAGUYA_PP_ARG_REPEAT(N)) {} KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_ARGS, KAGUYA_OBJECT_WRAPPER_CONSTRUCTOR_DEF) #undef KAGUYA_OBJECT_WRAPPER_CONSTRUCTOR_DEF #endif virtual const std::type_info &type() { return metatableType(); } virtual void *get() { return &object_; } virtual const void *cget() { return &object_; } private: T object_; }; struct ObjectSharedPointerWrapper : ObjectWrapperBase { template ObjectSharedPointerWrapper(const standard::shared_ptr &sptr) : object_(standard::const_pointer_cast< typename standard::remove_const::type>(sptr)), type_(metatableType()), shared_ptr_type_( metatableType< standard::shared_ptr::type> >()), const_value_(traits::is_const::value) {} #if KAGUYA_USE_RVALUE_REFERENCE template ObjectSharedPointerWrapper(standard::shared_ptr &&sptr) : object_(std::move(standard::const_pointer_cast< typename standard::remove_const::type>(sptr))), type_(metatableType()), shared_ptr_type_( metatableType< standard::shared_ptr::type> >()), const_value_(traits::is_const::value) {} #endif virtual const std::type_info &type() { return type_; } virtual void *get() { return const_value_ ? 0 : object_.get(); } virtual const void *cget() { return object_.get(); } standard::shared_ptr object() const { return const_value_ ? standard::shared_ptr() : object_; } standard::shared_ptr const_object() const { return object_; } const std::type_info &shared_ptr_type() const { return shared_ptr_type_; } virtual const std::type_info &native_type() { return metatableType >(); } virtual void *native_get() { return &object_; } private: standard::shared_ptr object_; const std::type_info &type_; const std::type_info &shared_ptr_type_; bool const_value_; }; template struct ObjectSmartPointerWrapper : ObjectWrapperBase { ObjectSmartPointerWrapper(const T &sptr) : object_(sptr) {} #if KAGUYA_USE_RVALUE_REFERENCE ObjectSmartPointerWrapper(T &&sptr) : object_(std::move(sptr)) {} #endif virtual const std::type_info &type() { return metatableType(); } virtual void *get() { return object_ ? &(*object_) : 0; } virtual const void *cget() { return object_ ? &(*object_) : 0; } virtual const std::type_info &native_type() { return metatableType(); } virtual void *native_get() { return &object_; } private: T object_; }; template struct ObjectPointerWrapper : ObjectWrapperBase { ObjectPointerWrapper(T *ptr) : object_(ptr) {} virtual const std::type_info &type() { return metatableType(); } virtual void *get() { if (traits::is_const::value) { return 0; } return const_cast(static_cast(object_)); } virtual const void *cget() { return object_; } ~ObjectPointerWrapper() {} protected: T *object_; }; // Customizable for ObjectPointerWrapper template struct ObjectPointerWrapperType { typedef ObjectPointerWrapper type; }; // for internal use struct PointerConverter { template static void *base_pointer_cast(void *from) { return static_cast(static_cast(from)); } template static standard::shared_ptr base_shared_pointer_cast(const standard::shared_ptr &from) { return standard::shared_ptr(standard::static_pointer_cast(from)); } typedef void *(*convert_function_type)(void *); typedef standard::shared_ptr (*shared_ptr_convert_function_type)( const standard::shared_ptr &); typedef std::pair convert_map_key; template void add_type_conversion() { add_function(metatableType(), metatableType(), &base_pointer_cast); add_function(metatableType >(), metatableType >(), &base_shared_pointer_cast); } template TO *get_pointer(ObjectWrapperBase *from) const { const std::type_info &to_type = metatableType(); // unreachable // if (to_type == from->type()) //{ // return static_cast(from->get()); //} std::map >::const_iterator match = function_map_.find( convert_map_key(to_type.name(), from->type().name())); if (match != function_map_.end()) { return static_cast(pcvt_list_apply(from->get(), match->second)); } return 0; } template const TO *get_const_pointer(ObjectWrapperBase *from) const { const std::type_info &to_type = metatableType(); // unreachable // if (to_type == from->type()) //{ // return static_cast(from->cget()); //} std::map >::const_iterator match = function_map_.find( convert_map_key(to_type.name(), from->type().name())); if (match != function_map_.end()) { return static_cast( pcvt_list_apply(const_cast(from->cget()), match->second)); } return 0; } template standard::shared_ptr get_shared_pointer(ObjectSharedPointerWrapper *from) const { const std::type_info &to_type = metatableType< standard::shared_ptr::type> >(); // unreachable // if (to_type == from->type()) // { // return //standard::static_pointer_cast(from->object()); // } const std::type_info &from_type = from->shared_ptr_type(); std::map >::const_iterator match = shared_ptr_function_map_.find( convert_map_key(to_type.name(), from_type.name())); if (match != shared_ptr_function_map_.end()) { standard::shared_ptr sptr = from->object(); if (!sptr && standard::is_const::value) { sptr = standard::const_pointer_cast(from->const_object()); } return standard::static_pointer_cast( pcvt_list_apply(sptr, match->second)); } return standard::shared_ptr(); } template T *get_pointer(ObjectWrapperBase *from, types::typetag) { return get_pointer(from); } template standard::shared_ptr get_pointer(ObjectWrapperBase *from, types::typetag >) { ObjectSharedPointerWrapper *ptr = dynamic_cast(from); if (ptr) { return get_shared_pointer(ptr); } return standard::shared_ptr(); } static int deleter(lua_State *state) { PointerConverter *ptr = (PointerConverter *)lua_touserdata(state, 1); ptr->~PointerConverter(); return 0; } static PointerConverter &get(lua_State *state) { static char kaguya_ptrcvt_key_ptr; util::ScopedSavedStack save(state); lua_pushlightuserdata(state, &kaguya_ptrcvt_key_ptr); lua_rawget(state, LUA_REGISTRYINDEX); if (lua_isuserdata(state, -1)) { return *static_cast(lua_touserdata(state, -1)); } else { void *ptr = lua_newuserdata( state, sizeof(PointerConverter)); // dummy data for gc call PointerConverter *converter = new (ptr) PointerConverter(); lua_createtable(state, 0, 2); lua_pushcclosure(state, &deleter, 0); lua_setfield(state, -2, "__gc"); lua_pushvalue(state, -1); lua_setfield(state, -2, "__index"); lua_setmetatable(state, -2); // set to userdata lua_pushlightuserdata(state, &kaguya_ptrcvt_key_ptr); lua_pushvalue(state, -2); lua_rawset(state, LUA_REGISTRYINDEX); return *converter; } } private: void add_function(const std::type_info &to_type, const std::type_info &from_type, convert_function_type f) { std::map > add_map; for (std::map >::iterator it = function_map_.begin(); it != function_map_.end(); ++it) { if (it->first.second == to_type.name()) { std::vector newlist; newlist.push_back(f); newlist.insert(newlist.end(), it->second.begin(), it->second.end()); add_map[convert_map_key(it->first.first, from_type.name())] = newlist; } } function_map_.insert(add_map.begin(), add_map.end()); std::vector flist; flist.push_back(f); function_map_[convert_map_key(to_type.name(), from_type.name())] = flist; } void add_function(const std::type_info &to_type, const std::type_info &from_type, shared_ptr_convert_function_type f) { std::map > add_map; for (std::map >::iterator it = shared_ptr_function_map_.begin(); it != shared_ptr_function_map_.end(); ++it) { if (it->first.second == to_type.name()) { std::vector newlist; newlist.push_back(f); newlist.insert(newlist.end(), it->second.begin(), it->second.end()); add_map[convert_map_key(it->first.first, from_type.name())] = newlist; } } shared_ptr_function_map_.insert(add_map.begin(), add_map.end()); std::vector flist; flist.push_back(f); shared_ptr_function_map_[convert_map_key(to_type.name(), from_type.name())] = flist; } void *pcvt_list_apply(void *ptr, const std::vector &flist) const { for (std::vector::const_iterator i = flist.begin(); i != flist.end(); ++i) { ptr = (*i)(ptr); } return ptr; } standard::shared_ptr pcvt_list_apply( standard::shared_ptr ptr, const std::vector &flist) const { for (std::vector::const_iterator i = flist.begin(); i != flist.end(); ++i) { #if KAGUYA_USE_CPP11 ptr = (*i)(std::move(ptr)); #else ptr = (*i)(ptr); #endif } return ptr; } PointerConverter() {} std::map > function_map_; std::map > shared_ptr_function_map_; PointerConverter(PointerConverter &); PointerConverter &operator=(PointerConverter &); }; namespace detail { inline bool object_wrapper_type_check(lua_State *l, int index) { #if KAGUYA_NO_USERDATA_TYPE_CHECK return lua_isuserdata(l, index) && !lua_islightuserdata(l, index); #endif if (lua_getmetatable(l, index)) { int type = lua_rawgetp_rtype(l, -1, metatable_name_key()); lua_pop(l, 2); return type == LUA_TSTRING; } return false; } } inline ObjectWrapperBase *object_wrapper(lua_State *l, int index) { if (detail::object_wrapper_type_check(l, index)) { ObjectWrapperBase *ptr = static_cast(lua_touserdata(l, index)); return ptr; } return 0; } template inline ObjectWrapperBase * object_wrapper(lua_State *l, int index, bool convert = true, types::typetag = types::typetag()) { if (detail::object_wrapper_type_check(l, index)) { ObjectWrapperBase *ptr = static_cast(lua_touserdata(l, index)); if (ptr->type() == metatableType()) { return ptr; } else if (convert) { PointerConverter &pcvt = PointerConverter::get(l); return pcvt.get_pointer(ptr, types::typetag()) ? ptr : 0; } return 0; } return 0; } template T *get_pointer(lua_State *l, int index, types::typetag) { int type = lua_type(l, index); if (type == LUA_TLIGHTUSERDATA) { return (T *)lua_topointer(l, index); } else if (type != LUA_TUSERDATA) { return 0; } else { ObjectWrapperBase *objwrapper = object_wrapper(l, index); if (objwrapper) { const std::type_info &to_type = metatableType(); if (objwrapper->type() == to_type) { return static_cast(objwrapper->get()); } if (objwrapper->native_type() == to_type) { return static_cast(objwrapper->native_get()); } else { PointerConverter &pcvt = PointerConverter::get(l); return pcvt.get_pointer(objwrapper); } } } return 0; } template const T *get_const_pointer(lua_State *l, int index, types::typetag) { int type = lua_type(l, index); if (type == LUA_TLIGHTUSERDATA) { return (T *)lua_topointer(l, index); } else if (type != LUA_TUSERDATA) { return 0; } else { ObjectWrapperBase *objwrapper = object_wrapper(l, index); if (objwrapper) { if (objwrapper->type() == metatableType()) { return static_cast(objwrapper->cget()); } else { PointerConverter &pcvt = PointerConverter::get(l); return pcvt.get_const_pointer(objwrapper); } } } return 0; } template const T *get_pointer(lua_State *l, int index, types::typetag) { return get_const_pointer(l, index, types::typetag()); } template standard::shared_ptr get_shared_pointer(lua_State *l, int index, types::typetag) { ObjectSharedPointerWrapper *ptr = dynamic_cast(object_wrapper(l, index)); if (ptr) { const std::type_info &from_type = ptr->shared_ptr_type(); const std::type_info &to_type = metatableType::type> >(); if (from_type == to_type) { if (standard::is_const::value) { return standard::static_pointer_cast( standard::const_pointer_cast(ptr->const_object())); } else { return standard::static_pointer_cast(ptr->object()); } } PointerConverter &pcvt = PointerConverter::get(l); return pcvt.get_shared_pointer(ptr); } return standard::shared_ptr(); } inline standard::shared_ptr get_shared_pointer(lua_State *l, int index, types::typetag) { ObjectSharedPointerWrapper *ptr = dynamic_cast(object_wrapper(l, index)); if (ptr) { return ptr->object(); } return standard::shared_ptr(); } inline standard::shared_ptr get_shared_pointer(lua_State *l, int index, types::typetag) { ObjectSharedPointerWrapper *ptr = dynamic_cast(object_wrapper(l, index)); if (ptr) { return ptr->const_object(); } return standard::shared_ptr(); } namespace class_userdata { template inline void destructor(T *pointer) { if (pointer) { pointer->~T(); } } inline bool get_metatable(lua_State *l, const std::type_info &typeinfo) { int ttype = lua_rawgetp_rtype( l, LUA_REGISTRYINDEX, metatable_type_table_key()); // get metatable registry table if (ttype != LUA_TTABLE) { lua_newtable(l); lua_rawsetp(l, LUA_REGISTRYINDEX, metatable_type_table_key()); return false; } int type = lua_rawgetp_rtype(l, -1, &typeinfo); lua_remove(l, -2); // remove metatable registry table return type != LUA_TNIL; } template bool get_metatable(lua_State *l) { return get_metatable(l, metatableType()); } template bool available_metatable(lua_State *l) { util::ScopedSavedStack save(l); return get_metatable(l); } inline bool newmetatable(lua_State *l, const std::type_info &typeinfo, const char *name) { if (get_metatable(l, typeinfo)) // already register { return false; // } lua_pop(l, 1); lua_rawgetp_rtype(l, LUA_REGISTRYINDEX, metatable_type_table_key()); // get metatable registry table int metaregindex = lua_absindex(l, -1); lua_createtable(l, 0, 2); lua_pushstring(l, name); lua_pushvalue(l, -1); lua_setfield(l, -3, "__name"); // metatable.__name = name lua_rawsetp(l, -2, metatable_name_key()); lua_pushvalue(l, -1); lua_rawsetp(l, metaregindex, &typeinfo); lua_remove(l, metaregindex); // remove metatable registry table return true; } template bool newmetatable(lua_State *l) { return newmetatable(l, metatableType(), metatableName().c_str()); } template inline int deleter(lua_State *state) { T *ptr = (T *)lua_touserdata(state, 1); ptr->~T(); return 0; } struct UnknownType {}; inline void setmetatable(lua_State *l, const std::type_info &typeinfo) { // if not available metatable, set unknown class metatable if (!get_metatable(l, typeinfo)) { lua_pop(l, 1); if (!get_metatable(l)) { lua_pop(l, 1); newmetatable(l); lua_pushcclosure(l, &deleter, 0); lua_setfield(l, -2, "__gc"); } } lua_setmetatable(l, -2); } template void setmetatable(lua_State *l) { setmetatable(l, metatableType()); } } template bool available_metatable(lua_State *l, types::typetag = types::typetag()) { return class_userdata::available_metatable(l); } namespace util { template inline bool object_checkType(lua_State *l, int index) { return object_wrapper(l, index) != 0; } template inline bool object_strictCheckType(lua_State *l, int index) { return object_wrapper(l, index, false) != 0; } template inline optional object_getopt(lua_State *l, int index) { const typename traits::remove_reference::type *pointer = get_const_pointer( l, index, types::typetag::type>()); if (!pointer) { return optional(); } return *pointer; } template inline T object_get(lua_State *l, int index) { const typename traits::remove_reference::type *pointer = get_const_pointer( l, index, types::typetag::type>()); if (!pointer) { throw LuaTypeMismatch(); } return *pointer; } template struct ConvertibleRegisterHelperProxy { template ConvertibleRegisterHelperProxy(DataType v) : holder_(new DataHolder(v)) {} operator To() { return holder_->get(); } private: struct DataHolderBase { virtual To get() const = 0; virtual ~DataHolderBase() {} }; template class DataHolder : public DataHolderBase { typedef typename traits::decay::type DataType; public: explicit DataHolder(DataType v) : data_(v) {} virtual To get() const { return To(data_); } private: DataType data_; }; standard::shared_ptr holder_; }; #if KAGUYA_USE_CPP11 template inline int object_push(lua_State *l, T &&v) { typedef ObjectWrapper::type> wrapper_type; void *storage = lua_newuserdata(l, sizeof(wrapper_type)); new (storage) wrapper_type(std::forward(v)); class_userdata::setmetatable(l); return 1; } namespace conv_helper_detail { template bool checkType(lua_State *, int) { return false; } template bool checkType(lua_State *l, int index) { return lua_type_traits::checkType(l, index) || checkType(l, index); } template bool strictCheckType(lua_State *, int) { return false; } template bool strictCheckType(lua_State *l, int index) { return lua_type_traits::strictCheckType(l, index) || strictCheckType(l, index); ; } template To get(lua_State *, int) { throw LuaTypeMismatch(); } template To get(lua_State *l, int index) { typedef optional::get_type> opt_type; if (auto opt = lua_type_traits::get(l, index)) { return *opt; } return get(l, index); } } template struct ConvertibleRegisterHelper { typedef To get_type; static bool checkType(lua_State *l, int index) { if (object_checkType(l, index)) { return true; } return conv_helper_detail::checkType(l, index); } static bool strictCheckType(lua_State *l, int index) { if (object_strictCheckType(l, index)) { return true; } return conv_helper_detail::strictCheckType(l, index); } static get_type get(lua_State *l, int index) { if (auto opt = object_getopt(l, index)) { return *opt; } return conv_helper_detail::get(l, index); } }; #else template inline int object_push(lua_State *l, T v) { typedef ObjectWrapper::type> wrapper_type; void *storage = lua_newuserdata(l, sizeof(wrapper_type)); new (storage) wrapper_type(v); class_userdata::setmetatable(l); return 1; } namespace conv_helper_detail { #define KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_REP(N) \ || lua_type_traits::checkType(state, index) #define KAGUYA_CONVERTIBLE_REG_HELPER_STRICT_CHECK_TYPE_REP(N) \ || lua_type_traits::strictCheckType(state, index) #define KAGUYA_CONVERTIBLE_REG_HELPER_GET_OPT_TYPEDEF(N) \ typedef optional::get_type> \ KAGUYA_PP_CAT(opt_type, N); #define KAGUYA_CONVERTIBLE_REG_HELPER_GET_REP(N) \ if (KAGUYA_PP_CAT(opt_type, N) opt = \ lua_type_traits::get(state, index)) { \ return *opt; \ } else template bool checkType(lua_State *, int, TypeTuple<>) { return false; } #define KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_DEF(N) \ template \ bool checkType(lua_State *state, int index, \ TypeTuple) { \ return false KAGUYA_PP_REPEAT( \ N, KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_REP); \ } KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_ARGS, KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_DEF) #undef KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_DEF template bool strictCheckType(lua_State *, int, TypeTuple<>) { return false; } #define KAGUYA_CONVERTIBLE_REG_HELPER_ST_CHECK_TYPE_DEF(N) \ template \ bool strictCheckType(lua_State *state, int index, \ TypeTuple) { \ return false KAGUYA_PP_REPEAT( \ N, KAGUYA_CONVERTIBLE_REG_HELPER_STRICT_CHECK_TYPE_REP); \ } KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_ARGS, KAGUYA_CONVERTIBLE_REG_HELPER_ST_CHECK_TYPE_DEF) #undef KAGUYA_CONVERTIBLE_REG_HELPER_ST_CHECK_TYPE_DEF #define KAGUYA_CONVERTIBLE_REG_HELPER_GET_DEF(N) \ template \ To get(lua_State *state, int index, \ TypeTuple) { \ KAGUYA_PP_REPEAT(N, KAGUYA_CONVERTIBLE_REG_HELPER_GET_OPT_TYPEDEF) \ KAGUYA_PP_REPEAT(N, KAGUYA_CONVERTIBLE_REG_HELPER_GET_REP) { \ throw LuaTypeMismatch(); \ } \ } KAGUYA_PP_REPEAT_DEF(KAGUYA_FUNCTION_MAX_ARGS, KAGUYA_CONVERTIBLE_REG_HELPER_GET_DEF) #undef KAGUYA_CONVERTIBLE_REG_HELPER_GET_DEF #undef KAGUYA_CONVERTIBLE_REG_HELPER_CHECK_TYPE_REP #undef KAGUYA_CONVERTIBLE_REG_HELPER_STRICT_CHECK_TYPE_REP #undef KAGUYA_CONVERTIBLE_REG_HELPER_ST_GET_REP #undef KAGUYA_CONVERTIBLE_REG_HELPER_GET_REP } #define KAGUYA_PP_CONVERTIBLE_REG_HELPER_DEF_REP(N) \ KAGUYA_PP_CAT(typename A, N) = null_type #define KAGUYA_PP_CONVERTIBLE_REG_HELPER_DEF_REPEAT(N) \ KAGUYA_PP_REPEAT_ARG(N, KAGUYA_PP_CONVERTIBLE_REG_HELPER_DEF_REP) template struct ConvertibleRegisterHelper { typedef To get_type; typedef TypeTuple conv_types; static bool checkType(lua_State *l, int index) { if (object_checkType(l, index)) { return true; } return conv_helper_detail::checkType(l, index, conv_types()); } static bool strictCheckType(lua_State *l, int index) { if (object_strictCheckType(l, index)) { return true; } return conv_helper_detail::strictCheckType(l, index, conv_types()); } static get_type get(lua_State *l, int index) { if (optional opt = object_getopt(l, index)) { return *opt; } return conv_helper_detail::get(l, index, conv_types()); } }; #undef KAGUYA_PP_CONVERTIBLE_REG_HELPER_DEF_REP #undef KAGUYA_PP_CONVERTIBLE_REG_HELPER_DEF_REPEAT #endif } }