From 656b438002e630da4ebf6b2148506445ef6a94f5 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 26 Mar 2014 10:52:56 -0600 Subject: [PATCH] First cast up chain, if that fails, cast down --- include/chaiscript/dispatchkit/boxed_cast.hpp | 11 +- .../dispatchkit/dynamic_cast_conversion.hpp | 115 +++++++++++------- unittests/inheritance.chai | 3 +- 3 files changed, 83 insertions(+), 46 deletions(-) diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index 345e89a..e3164d4 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -84,11 +84,18 @@ namespace chaiscript if (boost::is_polymorphic::type>::value && t_conversions) { try { + std::cout << "trying an up conversion " << typeid(Type).name() << std::endl; // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it // either way, we are not responsible if it doesn't work return detail::Cast_Helper::cast(t_conversions->boxed_dynamic_cast(bv), t_conversions); - } catch (const boost::bad_any_cast &) { - throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } catch (...) { + try { + std::cout << "trying a down conversion " << typeid(Type).name() << std::endl; + // try going the other way - down the inheritance graph + return detail::Cast_Helper::cast(t_conversions->boxed_dynamic_down_cast(bv), t_conversions); + } catch (const boost::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } } } else { // If it's not polymorphic, just throw the error, don't waste the time on the diff --git a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp index 93d435c..a43ce77 100644 --- a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp +++ b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp @@ -48,6 +48,7 @@ namespace chaiscript { public: virtual Boxed_Value convert(const Boxed_Value &derived) const = 0; + virtual Boxed_Value convert_down(const Boxed_Value &base) const = 0; const Type_Info &base() { @@ -72,6 +73,57 @@ namespace chaiscript }; + template + class Dynamic_Caster + { + public: + static Boxed_Value cast(const Boxed_Value &t_from) + { + if (t_from.get_type_info().bare_equal(user_type())) + { + if (t_from.is_pointer()) + { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + boost::shared_ptr data + = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, 0)); + if (!data) + { + throw std::bad_cast(); + } + + return Boxed_Value(data); + } else { + boost::shared_ptr data + = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, 0)); + + 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 (t_from.is_const()) + { + const From &d = detail::Cast_Helper::cast(t_from, 0); + const To &data = dynamic_cast(d); + return Boxed_Value(boost::cref(data)); + } else { + From &d = detail::Cast_Helper::cast(t_from, 0); + To &data = dynamic_cast(d); + return Boxed_Value(boost::ref(data)); + } + } + } else { + throw exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + } + } + }; + template class Dynamic_Conversion_Impl : public Dynamic_Conversion { @@ -81,50 +133,14 @@ namespace chaiscript { } + virtual Boxed_Value convert_down(const Boxed_Value &t_base) const + { + return Dynamic_Caster::cast(t_base); + } + virtual Boxed_Value convert(const Boxed_Value &t_derived) const { - if (t_derived.get_type_info().bare_equal(user_type())) - { - if (t_derived.is_pointer()) - { - // Dynamic cast out the contained boxed value, which we know is the type we want - if (t_derived.is_const()) - { - boost::shared_ptr data - = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_derived, 0)); - if (!data) - { - throw std::bad_cast(); - } - - return Boxed_Value(data); - } else { - boost::shared_ptr data - = boost::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_derived, 0)); - - 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 (t_derived.is_const()) - { - const Derived &d = detail::Cast_Helper::cast(t_derived, 0); - const Base &data = dynamic_cast(d); - return Boxed_Value(boost::cref(data)); - } else { - Derived &d = detail::Cast_Helper::cast(t_derived, 0); - Base &data = dynamic_cast(d); - return Boxed_Value(boost::ref(data)); - } - } - } else { - throw exception::bad_boxed_dynamic_cast(t_derived.get_type_info(), typeid(Base), "Unknown dynamic_cast_conversion"); - } + return Dynamic_Caster::cast(t_derived); } }; } @@ -155,7 +171,7 @@ namespace chaiscript bool dynamic_cast_converts(const Type_Info &base, const Type_Info &derived) const { - return has_conversion(base, derived); + return has_conversion(base, derived) || has_conversion(derived, base); } template @@ -170,6 +186,19 @@ namespace chaiscript } } + template + Boxed_Value boxed_dynamic_down_cast(const Boxed_Value &base) const + { + try { + return get_conversion(base.get_type_info(), user_type())->convert_down(base); + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(base.get_type_info(), typeid(Derived), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(base.get_type_info(), typeid(Derived), "Unable to perform dynamic_cast operation"); + } + } + + bool has_conversion(const Type_Info &base, const Type_Info &derived) const { chaiscript::detail::threading::shared_lock l(m_mutex); diff --git a/unittests/inheritance.chai b/unittests/inheritance.chai index 0105c68..99cf7c0 100644 --- a/unittests/inheritance.chai +++ b/unittests/inheritance.chai @@ -19,8 +19,9 @@ assert_equal(23, t.val) // in a shared_ptr. This is testing our ability // to detect that and do the down casting for the user automatically // at runtime -var d := derived_type_factory(); assert_equal(t.derived_only_func(), 19); + +var d := derived_type_factory(); assert_equal(d.derived_only_func(), 19);