#ifndef U_CORE_PROPERTY_H #define U_CORE_PROPERTY_H #include #include #include #include // Added #include #include #include "Core/Archives.h" #include "Core/Signal.h" #include "Core/Object.h" namespace uLib { /** * @brief Base class for properties to allow runtime listing and identification. */ class PropertyBase : public Object { public: virtual ~PropertyBase() {} virtual const std::string& GetName() const = 0; virtual const char* GetTypeName() const = 0; virtual std::string GetValueAsString() const = 0; virtual std::type_index GetTypeIndex() const = 0; // Added // Signal support signals: virtual void Updated() override { ULIB_SIGNAL_EMIT(PropertyBase::Updated); } // Serialization support for different uLib archives virtual void serialize(Archive::xml_oarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::xml_iarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::text_oarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::text_iarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) = 0; virtual void serialize(Archive::log_archive & ar, const unsigned int version) = 0; }; /** * @brief Template class for typed properties. */ template class Property : public PropertyBase { public: // PROXY: Use an existing variable as back-end storage Property(Object* owner, const std::string& name, T* valuePtr) : m_owner(owner), m_name(name), m_value(valuePtr), m_own(false) { if (m_owner) { m_owner->RegisterProperty(this); } } // MANAGED: Create and own internal storage Property(Object* owner, const std::string& name, const T& defaultValue = T()) : m_owner(owner), m_name(name), m_value(new T(defaultValue)), m_own(true) { if (m_owner) { m_owner->RegisterProperty(this); } } virtual ~Property() { if (m_own) delete m_value; } // Identification virtual const std::string& GetName() const override { return m_name; } virtual const char* GetTypeName() const override { return typeid(T).name(); } virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(T)); } std::string GetValueAsString() const override { try { return boost::lexical_cast(*m_value); } catch (const boost::bad_lexical_cast&) { std::stringstream ss; ss << *m_value; return ss.str(); } } // Accessors const T& Get() const { return *m_value; } void Set(const T& value) { if (*m_value != value) { *m_value = value; ULIB_SIGNAL_EMIT(Property::PropertyChanged); this->Updated(); if (m_owner) m_owner->Updated(); } } // Operators for seamless usage operator const T&() const { return *m_value; } Property& operator=(const T& value) { Set(value); return *this; } // Signals signals: virtual void PropertyChanged() { ULIB_SIGNAL_EMIT(Property::PropertyChanged); } // Serialization template void serialize_impl(ArchiveT & ar, const unsigned int version) { ar & boost::serialization::make_nvp(m_name.c_str(), *m_value); } void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_impl(ar, v); } void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_impl(ar, v); } private: std::string m_name; T* m_value; bool m_own; Object* m_owner; }; /** * @brief Conveninent typedefs for common property types. */ typedef Property StringProperty; typedef Property IntProperty; typedef Property UIntProperty; typedef Property LongProperty; typedef Property ULongProperty; typedef Property FloatProperty; typedef Property DoubleProperty; typedef Property BoolProperty; /** * @brief Macro to simplify property declaration within a class. * Usage: ULIB_PROPERTY(float, Width, 1.0f) * It creates a raw member m_Width and a Property proxy Width. */ #define ULIB_PROPERTY(type, name, defaultValue) \ type m_##name = defaultValue; \ Property name = Property(this, #name, &m_##name); } // namespace uLib namespace uLib { namespace Archive { class property_register_archive; } // namespace Archive } // namespace uLib namespace boost { namespace archive { namespace detail { template <> class interface_oarchive : public uLib_interface_oarchive {}; } // namespace detail } // namespace archive } // namespace boost namespace uLib { namespace Archive { /** * @brief A special archive that creates and registers Property proxies * for any member it encounters wrapped in HRP(). */ class property_register_archive : public boost::archive::detail::common_oarchive { Object* m_Object; public: friend class boost::archive::detail::interface_oarchive; friend class boost::archive::save_access; typedef boost::archive::detail::common_oarchive detail_common_oarchive; property_register_archive(Object* obj) : boost::archive::detail::common_oarchive(boost::archive::no_header), m_Object(obj) {} // Core logic: encounter HRP -> Create Dynamic Property template void save_override(const boost::serialization::hrp &t) { if (m_Object) { // We use const_cast because we are just creating a proxy to the member m_Object->RegisterDynamicProperty( new Property(m_Object, t.name(), &const_cast&>(t).value()) ); } } // Handle standard NVPs by recursing (important for base classes) template void save_override(const boost::serialization::nvp &t) { boost::archive::detail::common_oarchive::save_override(t.const_value()); } // Ignore everything else template void save_override(const T &t) {} // Required attribute overrides for common_oarchive void save_override(const boost::archive::object_id_type & t) {} void save_override(const boost::archive::object_reference_type & t) {} void save_override(const boost::archive::version_type & t) {} void save_override(const boost::archive::class_id_type & t) {} void save_override(const boost::archive::class_id_optional_type & t) {} void save_override(const boost::archive::class_id_reference_type & t) {} void save_override(const boost::archive::class_name_type & t) {} void save_override(const boost::archive::tracking_type & t) {} }; /** * @brief Convenience macro to automatically activate and register all HRP members * as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj) */ #define ULIB_ACTIVATE_PROPERTIES(obj) \ { uLib::Archive::property_register_archive _ar_tmp(&(obj)); (obj).serialize(_ar_tmp, 0); } } // namespace Archive } // namespace uLib #endif // U_CORE_PROPERTY_H