refactor: extend Object property system and implement recursive property discovery in Vtk::Puppet archive
This commit is contained in:
@@ -5,19 +5,24 @@
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
#include <typeindex> // Added
|
||||
#include <typeindex>
|
||||
#include <boost/serialization/nvp.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <vector>
|
||||
#include <boost/type_traits/is_class.hpp>
|
||||
#include <boost/mpl/bool.hpp>
|
||||
#include <boost/serialization/serialization.hpp>
|
||||
#include <set>
|
||||
#include <boost/type_traits/is_base_of.hpp>
|
||||
#include "Core/Archives.h"
|
||||
#include "Core/Signal.h"
|
||||
#include "Core/Object.h"
|
||||
|
||||
namespace uLib {
|
||||
|
||||
namespace Archive {
|
||||
class property_register_archive;
|
||||
class display_properties_archive;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Base class for properties to allow runtime listing and identification.
|
||||
*/
|
||||
@@ -27,7 +32,7 @@ public:
|
||||
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
|
||||
virtual std::type_index GetTypeIndex() const = 0;
|
||||
virtual const std::string& GetUnits() const = 0;
|
||||
virtual void SetUnits(const std::string& units) = 0;
|
||||
virtual const std::vector<std::string>& GetEnumLabels() const {
|
||||
@@ -61,42 +66,30 @@ public:
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int version) override = 0;
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int version) override = 0;
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int version) override = 0;
|
||||
|
||||
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) = 0;
|
||||
virtual void serialize(Archive::display_properties_archive & ar, const unsigned int v) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Template class for typed properties.
|
||||
*/
|
||||
template <typename T>
|
||||
class Property : public PropertyBase {
|
||||
public:
|
||||
// PROXY: Use an existing variable as back-end storage
|
||||
Property(Object* owner, const std::string& name, T* valuePtr, const std::string& units = "", const std::string& group = "")
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(valuePtr), m_own(false),
|
||||
m_HasRange(false), m_HasDefault(false), m_ReadOnly(false) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
if (m_owner) m_owner->RegisterProperty(this);
|
||||
}
|
||||
|
||||
// MANAGED: Create and own internal storage
|
||||
Property(Object* owner, const std::string& name, const T& defaultValue = T(), const std::string& units = "", const std::string& group = "")
|
||||
: m_owner(owner), m_name(name), m_units(units), m_group(group), m_value(new T(defaultValue)), m_own(true),
|
||||
m_HasRange(false), m_HasDefault(true), m_Default(defaultValue), m_ReadOnly(false) {
|
||||
if (m_owner) {
|
||||
m_owner->RegisterProperty(this);
|
||||
}
|
||||
if (m_owner) m_owner->RegisterProperty(this);
|
||||
}
|
||||
|
||||
virtual ~Property() {
|
||||
if (m_own) delete m_value;
|
||||
}
|
||||
virtual ~Property() { if (m_own) delete m_value; }
|
||||
|
||||
// Identification
|
||||
virtual const std::string& GetName() const override { return m_name; }
|
||||
@@ -107,36 +100,16 @@ public:
|
||||
virtual const std::string& GetGroup() const override { return m_group; }
|
||||
virtual void SetGroup(const std::string& group) override { m_group = group; }
|
||||
|
||||
|
||||
std::string GetValueAsString() const override {
|
||||
try {
|
||||
return boost::lexical_cast<std::string>(*m_value);
|
||||
} catch (const boost::bad_lexical_cast&) {
|
||||
std::stringstream ss;
|
||||
ss << *m_value;
|
||||
return ss.str();
|
||||
}
|
||||
try { return boost::lexical_cast<std::string>(*m_value); }
|
||||
catch (...) { std::stringstream ss; ss << *m_value; return ss.str(); }
|
||||
}
|
||||
|
||||
// Accessors
|
||||
const T& Get() const { return *m_value; }
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, void>::type
|
||||
ValidateT(T& val) {
|
||||
if (m_HasRange) {
|
||||
if (val < m_Min) val = m_Min;
|
||||
if (val > m_Max) val = m_Max;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, void>::type
|
||||
ValidateT(T& val) {
|
||||
}
|
||||
|
||||
void Set(const T& value) {
|
||||
T val = value;
|
||||
ValidateT<T>(val);
|
||||
if (m_HasRange) { if (val < m_Min) val = m_Min; if (val > m_Max) val = m_Max; }
|
||||
if (*m_value != val) {
|
||||
*m_value = val;
|
||||
ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged);
|
||||
@@ -150,65 +123,56 @@ public:
|
||||
void SetReadOnly(bool ro) { m_ReadOnly = ro; }
|
||||
|
||||
virtual bool IsReadOnly() const override { return m_ReadOnly; }
|
||||
|
||||
virtual bool HasRange() const override { return m_HasRange; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
|
||||
GetMinT() const { return (double)m_Min; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
|
||||
GetMinT() const { return 0.0; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<std::is_arithmetic<U>::value, double>::type
|
||||
GetMaxT() const { return (double)m_Max; }
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<!std::is_arithmetic<U>::value, double>::type
|
||||
GetMaxT() const { return 0.0; }
|
||||
|
||||
virtual double GetMin() const override { return GetMinT<T>(); }
|
||||
virtual double GetMax() const override { return GetMaxT<T>(); }
|
||||
virtual double GetMin() const override { return m_HasRange ? convert_to_double(m_Min) : 0.0; }
|
||||
virtual double GetMax() const override { return m_HasRange ? convert_to_double(m_Max) : 0.0; }
|
||||
|
||||
const T& GetMinTyped() const { return m_Min; }
|
||||
const T& GetMaxTyped() const { return m_Max; }
|
||||
|
||||
virtual bool HasDefault() const override { return m_HasDefault; }
|
||||
virtual std::string GetDefaultValueAsString() const override {
|
||||
try { return boost::lexical_cast<std::string>(m_Default); }
|
||||
catch (...) { return ""; }
|
||||
try { return boost::lexical_cast<std::string>(m_Default); } catch (...) { return ""; }
|
||||
}
|
||||
|
||||
// Operators for seamless usage
|
||||
// Operators
|
||||
operator const T&() const { return *m_value; }
|
||||
Property& operator=(const T& value) {
|
||||
Set(value);
|
||||
return *this;
|
||||
}
|
||||
Property& operator=(const T& value) { Set(value); return *this; }
|
||||
|
||||
// Signals
|
||||
signals:
|
||||
virtual void PropertyChanged() { ULIB_SIGNAL_EMIT(Property<T>::PropertyChanged); }
|
||||
|
||||
private:
|
||||
template <typename U>
|
||||
static double convert_to_double(const U& val) {
|
||||
return convert_to_double_impl(val, typename std::is_arithmetic<U>::type());
|
||||
}
|
||||
template <typename U>
|
||||
static double convert_to_double_impl(const U& val, std::true_type) { return (double)val; }
|
||||
template <typename U>
|
||||
static double convert_to_double_impl(const U& val, std::false_type) { return 0.0; }
|
||||
|
||||
public:
|
||||
// Serialization
|
||||
template <class ArchiveT>
|
||||
void serialize_impl(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp(m_name.c_str(), *m_value);
|
||||
void serialize_helper(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_hrp(m_name.c_str(), *m_value, m_units.c_str());
|
||||
}
|
||||
|
||||
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); }
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_helper(ar, v); }
|
||||
|
||||
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override;
|
||||
virtual void serialize(Archive::display_properties_archive & ar, const unsigned int v) override;
|
||||
|
||||
virtual void Updated() override {
|
||||
PropertyBase::Updated();
|
||||
this->PropertyChanged();
|
||||
}
|
||||
virtual void Updated() override { PropertyBase::Updated(); this->PropertyChanged(); }
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::string m_name;
|
||||
std::string m_units;
|
||||
std::string m_group;
|
||||
@@ -224,20 +188,7 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Conveninent typedefs for common property types.
|
||||
*/
|
||||
typedef Property<std::string> StringProperty;
|
||||
typedef Property<int> IntProperty;
|
||||
typedef Property<unsigned int> UIntProperty;
|
||||
typedef Property<long> LongProperty;
|
||||
typedef Property<unsigned long> ULongProperty;
|
||||
typedef Property<float> FloatProperty;
|
||||
typedef Property<double> DoubleProperty;
|
||||
typedef Property<Bool_t> BoolProperty;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Property specialized for enumerations, providing labels for GUI representations.
|
||||
* @brief Property specialized for enumerations.
|
||||
*/
|
||||
class EnumProperty : public Property<int> {
|
||||
public:
|
||||
@@ -248,68 +199,50 @@ public:
|
||||
const char* GetTypeName() const override { return "Enum"; }
|
||||
virtual std::type_index GetTypeIndex() const override { return std::type_index(typeid(EnumProperty)); }
|
||||
|
||||
template <class ArchiveT>
|
||||
void serialize_enum_helper(ArchiveT & ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_hrp_enum(m_name.c_str(), *m_value, m_Labels, m_units.c_str());
|
||||
}
|
||||
|
||||
virtual void serialize(Archive::xml_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::xml_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::text_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::text_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::hrt_oarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::hrt_iarchive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
virtual void serialize(Archive::log_archive & ar, const unsigned int v) override { serialize_enum_helper(ar, v); }
|
||||
|
||||
virtual void serialize(Archive::property_register_archive & ar, const unsigned int v) override;
|
||||
virtual void serialize(Archive::display_properties_archive & ar, const unsigned int v) override;
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_Labels;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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<type> name = Property<type>(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<uLib::Archive::property_register_archive>
|
||||
: public uLib_interface_oarchive<uLib::Archive::property_register_archive> {};
|
||||
} // 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<property_register_archive>
|
||||
{
|
||||
class property_register_archive
|
||||
: public boost::archive::detail::common_oarchive<property_register_archive> {
|
||||
protected:
|
||||
Object* m_Object;
|
||||
bool m_DisplayOnly;
|
||||
public:
|
||||
friend class boost::archive::detail::interface_oarchive<property_register_archive>;
|
||||
friend class boost::archive::save_access;
|
||||
|
||||
typedef boost::archive::detail::common_oarchive<property_register_archive> detail_common_oarchive;
|
||||
using boost::archive::detail::common_oarchive<property_register_archive>::save_override;
|
||||
|
||||
property_register_archive(Object* obj) :
|
||||
property_register_archive(Object* obj, bool displayOnly = false) :
|
||||
boost::archive::detail::common_oarchive<property_register_archive>(boost::archive::no_header),
|
||||
m_Object(obj) {}
|
||||
m_Object(obj), m_DisplayOnly(displayOnly) {
|
||||
if (obj) m_Visited.insert(dynamic_cast<const void*>(obj));
|
||||
}
|
||||
|
||||
template<class T> property_register_archive &operator&(const T &t) { this->save_override(t); return *this; }
|
||||
template<class T> property_register_archive &operator<<(const T &t) { this->save_override(t); return *this; }
|
||||
|
||||
std::string GetCurrentGroup() const {
|
||||
std::string group;
|
||||
@@ -320,77 +253,91 @@ public:
|
||||
return group;
|
||||
}
|
||||
|
||||
// Core logic: encounter HRP -> Create Dynamic Property
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp<T> &t) {
|
||||
if (m_Object) {
|
||||
Property<T>* p = new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
|
||||
if (t.has_default()) p->SetDefault(t.default_val());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
template<class T> void register_property(Property<T>& p) {
|
||||
save_property_impl(p.GetName().c_str(), const_cast<T&>(p.Get()), p.GetUnits().c_str(),
|
||||
p.HasRange(), p.GetMinTyped(), p.GetMaxTyped(), p.IsReadOnly());
|
||||
}
|
||||
|
||||
void register_enum_property(EnumProperty& p) {
|
||||
if (!m_Object) return;
|
||||
EnumProperty* newP = new EnumProperty(m_Object, p.GetName(), const_cast<int*>(&p.Get()), p.GetEnumLabels(), p.GetUnits(), GetCurrentGroup());
|
||||
newP->SetReadOnly(p.IsReadOnly());
|
||||
if (m_DisplayOnly) {
|
||||
m_Object->RegisterDisplayProperty(newP);
|
||||
Object* obj = m_Object;
|
||||
Object::connect(newP, &PropertyBase::Updated, [obj]() { obj->Updated(); });
|
||||
} else {
|
||||
m_Object->RegisterDynamicProperty(newP);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp_val<T> &t) {
|
||||
template<class T> void save_property_impl(const char* name, T& val, const char* units, bool hasRange, const T& minVal, const T& maxVal, bool isReadOnly) {
|
||||
if (m_Object) {
|
||||
// Note: hrp_val stores by value. Property usually points to existing data.
|
||||
// But here we are registering properties from HRP wrappers.
|
||||
// If it's hrp_val, it means it's an rvalue from a getter.
|
||||
// The hrp_val wrapper itself owns the value.
|
||||
// However, the property_register_archive is temporary.
|
||||
// This is a bit tricky. Usually HRP(rvalue) is meant for read-only display.
|
||||
// Let's use the address of the value in the wrapper, but mark it read-only.
|
||||
Property<T>* p = new Property<T>(m_Object, t.name(), &const_cast<boost::serialization::hrp_val<T>&>(t).value(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
if (t.has_range()) p->SetRange(t.min_val(), t.max_val());
|
||||
if (t.has_default()) p->SetDefault(t.default_val());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
Property<T>* p = new Property<T>(m_Object, name, &val, units ? units : "", GetCurrentGroup());
|
||||
set_range_helper(p, hasRange, minVal, maxVal, typename std::is_arithmetic<T>::type());
|
||||
p->SetReadOnly(isReadOnly);
|
||||
if (m_DisplayOnly) {
|
||||
m_Object->RegisterDisplayProperty(p);
|
||||
Object* obj = m_Object;
|
||||
Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); });
|
||||
} else {
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp_enum<T> &t) {
|
||||
template<class U> static void set_range_helper(Property<U>* p, bool hasRange, const U& minVal, const U& maxVal, std::true_type) { if (hasRange) p->SetRange(minVal, maxVal); }
|
||||
template<class U> static void set_range_helper(Property<U>* p, bool hasRange, const U& minVal, const U& maxVal, std::false_type) {}
|
||||
|
||||
template<class T> void save_override(const boost::serialization::hrp<T> &t) {
|
||||
// To handle T correctly without deduction issues, we assume T can be passed to save_property_impl
|
||||
T dummy = T(); // Ensure we can construct T
|
||||
save_property_impl(t.name(), const_cast<boost::serialization::hrp<T>&>(t).value(), t.units(), t.has_range(), t.has_range() ? t.min_val() : dummy, t.has_range() ? t.max_val() : dummy, t.is_read_only());
|
||||
}
|
||||
template<class T> void save_override(const boost::serialization::hrp_val<T> &t) {
|
||||
T dummy = T();
|
||||
save_property_impl(t.name(), const_cast<boost::serialization::hrp_val<T>&>(t).value(), t.units(), t.has_range(), t.has_range() ? t.min_val() : dummy, t.has_range() ? t.max_val() : dummy, t.is_read_only());
|
||||
}
|
||||
template<class T> void save_override(const boost::serialization::hrp_enum<T> &t) {
|
||||
if (m_Object) {
|
||||
EnumProperty* p = new EnumProperty(m_Object, t.name(), (int*)&const_cast<boost::serialization::hrp_enum<T>&>(t).value(), t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); }); }
|
||||
else { m_Object->RegisterDynamicProperty(p); }
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::hrp_enum_val<T> &t) {
|
||||
template<class T> void save_override(const boost::serialization::hrp_enum_val<T> &t) {
|
||||
if (m_Object) {
|
||||
EnumProperty* p = new EnumProperty(m_Object, t.name(), (int*)&const_cast<boost::serialization::hrp_enum_val<T>&>(t).value(), t.labels(), t.units() ? t.units() : "", GetCurrentGroup());
|
||||
p->SetReadOnly(t.is_read_only());
|
||||
m_Object->RegisterDynamicProperty(p);
|
||||
if (m_DisplayOnly) { m_Object->RegisterDisplayProperty(p); Object* obj = m_Object; Object::connect(p, &PropertyBase::Updated, [obj]() { obj->Updated(); }); }
|
||||
else { m_Object->RegisterDynamicProperty(p); }
|
||||
}
|
||||
}
|
||||
|
||||
// Handle standard NVPs by recursing (important for base classes)
|
||||
template<class T>
|
||||
void save_override(const boost::serialization::nvp<T> &t) {
|
||||
template<class T> void save_override(const boost::serialization::nvp<T> &t) {
|
||||
if (t.name()) m_GroupStack.push_back(t.name());
|
||||
this->save_helper(t.const_value(), typename boost::is_class<T>::type());
|
||||
if (t.name()) m_GroupStack.pop_back();
|
||||
}
|
||||
|
||||
// Recursion for nested classes, ignore primitives
|
||||
template<class T>
|
||||
void save_override(const T &t) {
|
||||
this->save_helper(t, typename boost::is_class<T>::type());
|
||||
void save_override(const std::string &t) {}
|
||||
template<class T> void save_override(T * const & t) {
|
||||
if (!t) return;
|
||||
this->save_pointer_helper(t, typename boost::is_base_of<Object, T>::type());
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void save_helper(const T &t, boost::mpl::true_) {
|
||||
boost::serialization::serialize_adl(*this, const_cast<T&>(t), 0);
|
||||
template<class T> void save_pointer_helper(T* t, boost::mpl::true_) {
|
||||
const void* ptr = dynamic_cast<const void*>(t);
|
||||
if (m_Visited.find(ptr) != m_Visited.end()) return;
|
||||
m_Visited.insert(ptr);
|
||||
this->save_override(*t);
|
||||
}
|
||||
template<class T> void save_pointer_helper(T* t, boost::mpl::false_) {}
|
||||
template<class T> void save_override(const T &t) { this->save_helper(t, typename boost::is_class<T>::type()); }
|
||||
template<class T> void save_helper(const T &t, boost::mpl::true_) { boost::serialization::serialize_adl(*this, const_cast<T&>(t), 0); }
|
||||
void save_helper(const std::string &t, boost::mpl::true_) {}
|
||||
template<class T> void save_helper(const T &t, boost::mpl::false_) {}
|
||||
|
||||
template<class T>
|
||||
void save_helper(const T &t, boost::mpl::false_) {}
|
||||
|
||||
// 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) {}
|
||||
@@ -400,30 +347,45 @@ public:
|
||||
void save_override(const boost::archive::class_name_type & t) {}
|
||||
void save_override(const boost::archive::tracking_type & t) {}
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::vector<std::string> m_GroupStack;
|
||||
std::set<const void*> m_Visited;
|
||||
};
|
||||
|
||||
class display_properties_archive : public property_register_archive {
|
||||
public:
|
||||
friend class boost::archive::detail::interface_oarchive<display_properties_archive>;
|
||||
display_properties_archive(Object* obj) : property_register_archive(obj, true) {}
|
||||
};
|
||||
|
||||
} // namespace Archive
|
||||
} // namespace uLib
|
||||
|
||||
namespace uLib {
|
||||
|
||||
template <typename T>
|
||||
inline void Property<T>::serialize(Archive::property_register_archive & ar, const unsigned int v) {
|
||||
ar.register_property(*this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Property<T>::serialize(Archive::display_properties_archive & ar, const unsigned int v) {
|
||||
ar.register_property(*this);
|
||||
}
|
||||
|
||||
inline void EnumProperty::serialize(Archive::property_register_archive & ar, const unsigned int v) {
|
||||
ar.register_enum_property(*this);
|
||||
}
|
||||
|
||||
inline void EnumProperty::serialize(Archive::display_properties_archive & ar, const unsigned int v) {
|
||||
ar.register_enum_property(*this);
|
||||
}
|
||||
|
||||
namespace Archive {
|
||||
|
||||
/**
|
||||
* @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)); _ar_tmp & (obj); }
|
||||
|
||||
/**
|
||||
* @brief Declares a private member that automatically calls ULIB_ACTIVATE_PROPERTIES
|
||||
* in every constructor of the class. Place this macro as the last declaration
|
||||
* inside the class body (before the closing brace).
|
||||
*
|
||||
* Usage: ULIB_DECLARE_PROPERTIES(ClassName)
|
||||
*
|
||||
* This replaces per-constructor ULIB_ACTIVATE_PROPERTIES(*this) calls.
|
||||
* RegisterDynamicProperty deduplicates by qualified name, so re-registration
|
||||
* from inherited activators in a hierarchy is safe.
|
||||
*/
|
||||
#define ULIB_DECLARE_PROPERTIES(SelfType) \
|
||||
private: \
|
||||
struct _PropActivator { \
|
||||
@@ -434,6 +396,18 @@ private: \
|
||||
} _prop_activator{this};
|
||||
|
||||
} // namespace Archive
|
||||
|
||||
template <class ArchiveT>
|
||||
void serialize_properties_helper(ArchiveT &ar, const std::vector<PropertyBase*> &props, unsigned int version) {
|
||||
for (auto* prop : props) prop->serialize(ar, version);
|
||||
}
|
||||
|
||||
template <class ArchiveT>
|
||||
void Object::serialize(ArchiveT &ar, const unsigned int version) {
|
||||
ar & boost::serialization::make_nvp("InstanceName", this->GetInstanceName());
|
||||
serialize_properties_helper(ar, this->GetProperties(), version);
|
||||
}
|
||||
|
||||
} // namespace uLib
|
||||
|
||||
#endif // U_CORE_PROPERTY_H
|
||||
|
||||
Reference in New Issue
Block a user