First cast up chain, if that fails, cast down

This commit is contained in:
Jason Turner
2014-03-26 10:52:56 -06:00
parent 56b036052f
commit 656b438002
3 changed files with 83 additions and 46 deletions

View File

@@ -84,11 +84,18 @@ namespace chaiscript
if (boost::is_polymorphic<typename detail::Stripped_Type<Type>::type>::value && t_conversions) if (boost::is_polymorphic<typename detail::Stripped_Type<Type>::type>::value && t_conversions)
{ {
try { 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 // 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 // either way, we are not responsible if it doesn't work
return detail::Cast_Helper<Type>::cast(t_conversions->boxed_dynamic_cast<Type>(bv), t_conversions); return detail::Cast_Helper<Type>::cast(t_conversions->boxed_dynamic_cast<Type>(bv), t_conversions);
} catch (const boost::bad_any_cast &) { } catch (...) {
throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); 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<Type>::cast(t_conversions->boxed_dynamic_down_cast<Type>(bv), t_conversions);
} catch (const boost::bad_any_cast &) {
throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type));
}
} }
} else { } else {
// If it's not polymorphic, just throw the error, don't waste the time on the // If it's not polymorphic, just throw the error, don't waste the time on the

View File

@@ -48,6 +48,7 @@ namespace chaiscript
{ {
public: public:
virtual Boxed_Value convert(const Boxed_Value &derived) const = 0; 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() const Type_Info &base()
{ {
@@ -72,6 +73,57 @@ namespace chaiscript
}; };
template<typename From, typename To>
class Dynamic_Caster
{
public:
static Boxed_Value cast(const Boxed_Value &t_from)
{
if (t_from.get_type_info().bare_equal(user_type<From>()))
{
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<const To> data
= boost::dynamic_pointer_cast<const To>(detail::Cast_Helper<boost::shared_ptr<const From> >::cast(t_from, 0));
if (!data)
{
throw std::bad_cast();
}
return Boxed_Value(data);
} else {
boost::shared_ptr<To> data
= boost::dynamic_pointer_cast<To>(detail::Cast_Helper<boost::shared_ptr<From> >::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<const From &>::cast(t_from, 0);
const To &data = dynamic_cast<const To &>(d);
return Boxed_Value(boost::cref(data));
} else {
From &d = detail::Cast_Helper<From &>::cast(t_from, 0);
To &data = dynamic_cast<To &>(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<typename Base, typename Derived> template<typename Base, typename Derived>
class Dynamic_Conversion_Impl : public Dynamic_Conversion 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<Base, Derived>::cast(t_base);
}
virtual Boxed_Value convert(const Boxed_Value &t_derived) const virtual Boxed_Value convert(const Boxed_Value &t_derived) const
{ {
if (t_derived.get_type_info().bare_equal(user_type<Derived>())) return Dynamic_Caster<Derived, Base>::cast(t_derived);
{
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<const Base> data
= boost::dynamic_pointer_cast<const Base>(detail::Cast_Helper<boost::shared_ptr<const Derived> >::cast(t_derived, 0));
if (!data)
{
throw std::bad_cast();
}
return Boxed_Value(data);
} else {
boost::shared_ptr<Base> data
= boost::dynamic_pointer_cast<Base>(detail::Cast_Helper<boost::shared_ptr<Derived> >::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<const Derived &>::cast(t_derived, 0);
const Base &data = dynamic_cast<const Base &>(d);
return Boxed_Value(boost::cref(data));
} else {
Derived &d = detail::Cast_Helper<Derived &>::cast(t_derived, 0);
Base &data = dynamic_cast<Base &>(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");
}
} }
}; };
} }
@@ -155,7 +171,7 @@ namespace chaiscript
bool dynamic_cast_converts(const Type_Info &base, const Type_Info &derived) const 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<typename Base> template<typename Base>
@@ -170,6 +186,19 @@ namespace chaiscript
} }
} }
template<typename Derived>
Boxed_Value boxed_dynamic_down_cast(const Boxed_Value &base) const
{
try {
return get_conversion(base.get_type_info(), user_type<Derived>())->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 bool has_conversion(const Type_Info &base, const Type_Info &derived) const
{ {
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex); chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);

View File

@@ -19,8 +19,9 @@ assert_equal(23, t.val)
// in a shared_ptr<TestBaseType>. This is testing our ability // in a shared_ptr<TestBaseType>. This is testing our ability
// to detect that and do the down casting for the user automatically // to detect that and do the down casting for the user automatically
// at runtime // at runtime
var d := derived_type_factory();
assert_equal(t.derived_only_func(), 19); assert_equal(t.derived_only_func(), 19);
var d := derived_type_factory();
assert_equal(d.derived_only_func(), 19); assert_equal(d.derived_only_func(), 19);