First cast up chain, if that fails, cast down
This commit is contained in:
@@ -84,11 +84,18 @@ namespace chaiscript
|
||||
if (boost::is_polymorphic<typename detail::Stripped_Type<Type>::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<Type>::cast(t_conversions->boxed_dynamic_cast<Type>(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<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 {
|
||||
// If it's not polymorphic, just throw the error, don't waste the time on the
|
||||
|
@@ -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<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>
|
||||
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
|
||||
{
|
||||
if (t_derived.get_type_info().bare_equal(user_type<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");
|
||||
}
|
||||
return Dynamic_Caster<Derived, Base>::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<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
|
||||
{
|
||||
chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l(m_mutex);
|
||||
|
@@ -19,8 +19,9 @@ assert_equal(23, t.val)
|
||||
// in a shared_ptr<TestBaseType>. 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);
|
||||
|
||||
|
Reference in New Issue
Block a user