#ifndef __chaiscriptdispatchkit_dynamic_cast_conversion_hpp__ #define __chaiscriptdispatchkit_dynamic_cast_conversion_hpp__ #include "type_info.hpp" #include "boxed_value.hpp" #include "boxed_cast_helper.hpp" #include "bad_boxed_cast.hpp" #include #include #include #ifndef CHAISCRIPT_NO_THREADS #include #endif namespace chaiscript { class bad_boxed_dynamic_cast : public bad_boxed_cast { public: bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, const std::string &t_what) : bad_boxed_cast(t_from, t_to, t_what) { } bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) throw() : bad_boxed_cast(t_from, t_to) { } bad_boxed_dynamic_cast(const std::string &w) throw() : bad_boxed_cast(w) { } }; namespace detail { class Dynamic_Conversion { public: virtual Boxed_Value convert(const Boxed_Value &derived) = 0; const Type_Info &base() { return m_base; } const Type_Info &derived() { return m_derived; } protected: Dynamic_Conversion(const Type_Info &t_base, const Type_Info &t_derived) : m_base(t_base), m_derived(t_derived) { } private: Type_Info m_base; Type_Info m_derived; }; template class Dynamic_Conversion_Impl : public Dynamic_Conversion { public: Dynamic_Conversion_Impl() : Dynamic_Conversion(user_type(), user_type()) { } virtual Boxed_Value convert(const Boxed_Value &derived) { if (derived.get_type_info().bare_equal(user_type())) { if (derived.is_pointer()) { // Dynamic cast out the contained boxed value, which we know is the type we want if (derived.is_const()) { boost::shared_ptr data = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(derived)); if (!data) { throw std::bad_cast(); } return Boxed_Value(data); } else { boost::shared_ptr data = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(derived)); if (!data) { throw std::bad_cast(); } return Boxed_Value(data); } } else { // Pull the reference out of the contained boxed value, which we know is the type we want if (derived.is_const()) { const Derived &d = detail::Cast_Helper::cast(derived); const Base &data = dynamic_cast(d); return Boxed_Value(boost::cref(data)); } else { Derived &d = detail::Cast_Helper::cast(derived); Base &data = dynamic_cast(d); return Boxed_Value(boost::ref(data)); } } } else { throw bad_boxed_dynamic_cast(derived.get_type_info(), typeid(Base), "Unknown dynamic_cast_conversion"); } } }; class Dynamic_Conversions { public: static inline Dynamic_Conversions &get() { static Dynamic_Conversions obj; return obj; } template static boost::shared_ptr create() { boost::shared_ptr conversion(new Dynamic_Conversion_Impl()); /// \todo this is a hack and a kludge. The idea is to make sure that /// the conversion is registered both in the module's notion of the static conversion object /// and in the global notion of the static conversion object /// someday this will almost certainly have to change. Maybe it is time for ChaiScript /// to become a library? Dynamic_Conversions::get().add_conversion(conversion); return conversion; } template void cleanup(InItr begin, const InItr &end) { #ifndef CHAISCRIPT_NO_THREADS boost::unique_lock l(m_mutex); #endif while (begin != end) { if (begin->unique()) { m_conversions.erase(begin->get()); } ++begin; } } void add_conversion(const boost::shared_ptr &conversion) { #ifndef CHAISCRIPT_NO_THREADS boost::unique_lock l(m_mutex); #endif m_conversions.insert(conversion.get()); } bool has_conversion(const Type_Info &base, const Type_Info &derived) { #ifndef CHAISCRIPT_NO_THREADS boost::shared_lock l(m_mutex); #endif return find(base, derived) != m_conversions.end(); } Dynamic_Conversion *get_conversion(const Type_Info &base, const Type_Info &derived) { #ifndef CHAISCRIPT_NO_THREADS boost::shared_lock l(m_mutex); #endif std::set::const_iterator itr = find(base, derived); if (itr != m_conversions.end()) { return *itr; } else { throw std::out_of_range("No such conversion exists from " + derived.bare_name() + " to " + base.bare_name()); } } private: Dynamic_Conversions() {} std::set::const_iterator find( const Type_Info &base, const Type_Info &derived) { for (std::set::const_iterator itr = m_conversions.begin(); itr != m_conversions.end(); ++itr) { if ((*itr)->base().bare_equal(base) && (*itr)->derived().bare_equal(derived)) { return itr; } } return m_conversions.end(); } #ifndef CHAISCRIPT_NO_THREADS boost::shared_mutex m_mutex; #endif std::set m_conversions; }; } typedef boost::shared_ptr Dynamic_Cast_Conversion; /// Create a new base class registration for applying to a module or to the chaiscript engine /// Currently, due to limitations in module loading on Windows, and for the sake of portability, /// if you have a type that is introduced in a loadable module and is used by multiple modules /// (through a tertiary dll that is shared between the modules, static linking the new type /// into both loadable modules would not be portable), you need to register the base type /// relationship in all modules that use the newly added type in a polymorphic way. /// \todo Move share static type registration code into a mechanism that allows it to be properly /// shared by all modules template Dynamic_Cast_Conversion base_class() { //Can only be used with related polymorphic types //may be expanded some day to support conversions other than child -> parent BOOST_STATIC_ASSERT((boost::is_base_of::value)); BOOST_STATIC_ASSERT(boost::is_polymorphic::value); BOOST_STATIC_ASSERT(boost::is_polymorphic::value); return detail::Dynamic_Conversions::create(); } template bool dynamic_cast_converts() { return dynamic_cast_converts(user_type(), user_type()); } bool dynamic_cast_converts(const Type_Info &base, const Type_Info &derived) { return detail::Dynamic_Conversions::get().has_conversion(base, derived); } template Boxed_Value boxed_dynamic_cast(const Boxed_Value &derived) { try { return detail::Dynamic_Conversions::get().get_conversion(user_type(), derived.get_type_info())->convert(derived); } catch (const std::out_of_range &) { throw bad_boxed_dynamic_cast(derived.get_type_info(), typeid(Base), "No known conversion"); } catch (const std::bad_cast &) { throw bad_boxed_dynamic_cast(derived.get_type_info(), typeid(Base), "Unable to perform dynamic_cast operation"); } } } #endif