diff --git a/include/ki/pclass/StaticProperty.h b/include/ki/pclass/StaticProperty.h index 7bb44e2..0d0e3dd 100644 --- a/include/ki/pclass/StaticProperty.h +++ b/include/ki/pclass/StaticProperty.h @@ -231,7 +231,7 @@ namespace pclass // ValueT does derive from PropertyClass, but we don't store a pointer, // so we need to copy the value in. - prop.m_value = *object; + prop.m_value = *dynamic_cast(object); delete object; } }; diff --git a/include/ki/pclass/Value.h b/include/ki/pclass/Value.h index 5b1d964..8514f1c 100644 --- a/include/ki/pclass/Value.h +++ b/include/ki/pclass/Value.h @@ -1,15 +1,74 @@ #pragma once #include -#include #include +#include +#include "ki/util/exception.h" namespace ki { namespace pclass { + class Value; + + namespace detail + { + template + struct value_caster_helper; + + /** + * TODO: Documentation + */ + struct value_caster_base + { + virtual ~value_caster_base() {} + virtual Value cast(const type_info &dst_type, Value &v) const = 0; + + protected: + /** + * Provides a nice way for specialized casters to throw with a + * consistent error when casting is not possible. + */ + Value bad_cast(const type_info &src_type, const type_info &dst_type) const; + }; + + /** + * TODO: Documentation + */ + struct value_caster + { + // Allow Value to call the default constructor and make + friend Value; + + /** + * @tparam DstT The cast destination type. + * @param[in] value The Value that is being casted to the destination type. + * @returns A Value with a reference that has been casted to the destination type. + */ + template + Value cast(Value &value) const; + + private: + value_caster_base *m_caster; + + explicit value_caster(value_caster_base *caster = nullptr) + { + m_caster = caster; + } + + /** + * @tparam SrcT + */ + template + static value_caster make() + { + return value_caster(new value_caster_helper()); + } + }; + } + /** - * A wrapper around a void pointer that ensures type safety. - */ + * A wrapper around a void pointer that ensures type safety. + */ class Value { public: @@ -18,6 +77,7 @@ namespace pclass { m_value_ptr = static_cast(&value); m_type_hash = typeid(value).hash_code(); + m_caster = detail::value_caster::make(); } template @@ -25,16 +85,18 @@ namespace pclass { m_value_ptr = const_cast(static_cast(&value)); m_type_hash = typeid(value).hash_code(); + m_caster = detail::value_caster::make(); } Value(Value &&o) noexcept - : m_value_ptr(std::move(m_value_ptr)) + : m_value_ptr(std::move(o.m_value_ptr)) , m_type_hash(std::move(o.m_type_hash)) + , m_caster(std::move(o.m_caster)) {} /** - * @return Whether or not the value being held is of type T. - */ + * @return Whether or not the value being held is of type T. + */ template bool is() const { @@ -43,32 +105,63 @@ namespace pclass } /** - * @return A reference to the value being held as the specified type. - */ + * @return A reference to the value being held as the specified type. + */ template const T &get() const { - // Make sure that this is allowed + // Do we need to attempt casting? if (!is()) - throw std::runtime_error("Type mismatch in Value::get() call."); + return m_caster.cast(*this).get(); return *static_cast(m_value_ptr); } /** - * @return A reference to the value being held as the specified type. - */ + * @return A reference to the value being held as the specified type. + */ template T &get() { - // Make sure that this is allowed + // Do we need to attempt casting? if (!is()) - throw std::runtime_error("Type mismatch in Value::get() call."); + return m_caster.cast(*this).get(); return *static_cast(m_value_ptr); } private: void *m_value_ptr; std::size_t m_type_hash; + detail::value_caster m_caster; }; + + namespace detail + { + inline Value value_caster_base::bad_cast( + const type_info& src_type, const type_info& dst_type) const + { + std::ostringstream oss; + oss << "Cannot cast Value from " << src_type.name() + << " to " << dst_type.name() << "."; + throw runtime_error(oss.str()); + } + + template + Value value_caster::cast(Value& value) const + { + return m_caster->cast(typeid(DstT), value); + } + + /** + * TODO: Documentation + */ + template + struct value_caster_helper : value_caster_base + { + Value cast(const type_info &dst_type, Value& value) const override + { + return bad_cast(typeid(SrcT), dst_type); + } + }; + } +} } -} \ No newline at end of file diff --git a/include/ki/pclass/VectorProperty.h b/include/ki/pclass/VectorProperty.h index 2836fed..a44cd44 100644 --- a/include/ki/pclass/VectorProperty.h +++ b/include/ki/pclass/VectorProperty.h @@ -190,7 +190,7 @@ namespace pclass // ValueT does derive from PropertyClass, and we have an instance of ValueT, // so we can cast down to a PropertyClass pointer. - return dynamic_cast(&prop.at(index)); + return dynamic_cast(&prop.at(index)); } static void set_object(VectorProperty &prop, PropertyClass *object, const int index) @@ -208,7 +208,7 @@ namespace pclass // ValueT does derive from PropertyClass, but we don't store a pointer, // so we need to copy the value in. - prop.at(index) = dynamic_cast(*object); + prop.at(index) = *dynamic_cast(object); delete object; } };