fix serialization for properties unintrusive

This commit is contained in:
AndreaRigoni
2026-04-01 19:59:37 +00:00
parent 8e6e332217
commit 9118afdd13
7 changed files with 312 additions and 182 deletions

View File

@@ -59,6 +59,8 @@ class xml_iarchive;
class xml_oarchive;
class text_iarchive;
class text_oarchive;
class hrt_iarchive;
class hrt_oarchive;
class log_archive;
} // namespace Archive
@@ -150,7 +152,7 @@ public:
Archive *This() { return static_cast<Archive *>(this); }
template <class T>
const basic_pointer_iserializer *register_type(T * = NULL) {
const basic_pointer_iserializer *register_type(T * = nullptr) {
const basic_pointer_iserializer &bpis = boost::serialization::singleton<
pointer_iserializer<Archive, T>>::get_const_instance();
this->This()->register_basic_serializer(bpis.get_basic_serializer());
@@ -161,15 +163,36 @@ public:
return *this->This();
}
template <class T>
Archive &operator>>(const boost::serialization::nvp<T> &t) {
this->This()->load_override(t);
return *this->This();
}
template <class T>
Archive &operator>>(const boost::serialization::hrp<T> &t) {
this->This()->load_override(const_cast<boost::serialization::hrp<T> &>(t));
return *this->This();
}
// the & operator
template <class T> Archive &operator&(T &t) { return *(this->This()) >> t; }
template <class T>
Archive &operator&(const boost::serialization::nvp<T> &t) {
return *(this->This()) >> t;
}
template <class T>
Archive &operator&(const boost::serialization::hrp<T> &t) {
return *(this->This()) >> t;
}
// the == operator
template <class T> Archive &operator==(T &t) { return this->operator&(t); }
// the != operator for human readable access
template <class T> Archive &operator!=(T &t) {
std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
return *this->This();
}
};
@@ -191,7 +214,7 @@ public:
Archive *This() { return static_cast<Archive *>(this); }
template <class T>
const basic_pointer_oserializer *register_type(const T * = NULL) {
const basic_pointer_oserializer *register_type(const T * = nullptr) {
const basic_pointer_oserializer &bpos = boost::serialization::singleton<
pointer_oserializer<Archive, T>>::get_const_instance();
this->This()->register_basic_serializer(bpos.get_basic_serializer());
@@ -215,7 +238,6 @@ public:
// the != operator for human readable access
template <class T> Archive &operator!=(T &t) {
std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
return *this->This();
}
};
@@ -240,23 +262,22 @@ template <>
class interface_oarchive<uLib::Archive::text_oarchive>
: public uLib_interface_oarchive<uLib::Archive::text_oarchive> {};
template <>
class interface_iarchive<uLib::Archive::hrt_iarchive>
: public uLib_interface_iarchive<uLib::Archive::hrt_iarchive> {};
template <>
class interface_oarchive<uLib::Archive::hrt_oarchive>
: public uLib_interface_oarchive<uLib::Archive::hrt_oarchive> {};
template <>
class interface_iarchive<uLib::Archive::log_archive>
: public uLib_interface_iarchive<uLib::Archive::log_archive> {};
template <>
class interface_oarchive<uLib::Archive::log_archive>
: public uLib_interface_oarchive<uLib::Archive::log_archive> {};
//// Veritical repetition macro // FINIRE !!!!!!!!!!!!!!!!!!!!!!!!!
// #define _DECL_INTERFACE_ARCHIVE_V(vz,vn,vdata) \
// template <class TypeSeq> \
// struct inherit_nofold<TypeSeq,BOOST_PP_INC(vn)> : \
// BOOST_PP_REPEAT(BOOST_PP_INC(vn),_INERIT_NOFOLD_H,~) \
// {};
//// Multiple size declaration //
// BOOST_PP_REPEAT(ULIB_CFG_MPL_INERIT_NOFOLD_MAXSIZE,_INERIT_NOFOLD_V,~)
// #undef _INERIT_NOFOLD_H
// #undef _INERIT_NOFOLD_V
} // namespace detail
} // namespace archive
} // namespace boost
@@ -275,36 +296,6 @@ class interface_oarchive<uLib::Archive::log_archive>
namespace boost {
namespace archive {
// template<class Archive>
// inline void load_const_override(Archive & ar, const char *t ){
// typedef typename mpl::identity<detail::load_non_pointer_type<Archive>
// >::type typex; typex::invoke(ar, t);
// }
// template<class Archive, class T>
// inline void load(Archive & ar, T &t){
// // if this assertion trips. It means we're trying to load a
// // const object with a compiler that doesn't have correct
// // funtion template ordering. On other compilers, this is
// // handled below.
// // detail::check_const_loading< T >();
// typedef
// BOOST_DEDUCED_TYPENAME mpl::eval_if<is_pointer< T >,
// mpl::identity<detail::load_pointer_type<Archive> >
// ,//else
// BOOST_DEDUCED_TYPENAME mpl::eval_if<is_array< T >,
// mpl::identity<detail::load_array_type<Archive> >
// ,//else
// BOOST_DEDUCED_TYPENAME mpl::eval_if<is_enum< T >,
// mpl::identity<detail::load_enum_type<Archive> >
// ,//else
// mpl::identity<detail::load_non_pointer_type<Archive> >
// >
// >
// >::type typex;
// typex::invoke(ar, t);
// }
} // namespace archive
} // namespace boost
@@ -312,22 +303,6 @@ namespace uLib {
namespace Archive {
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// POLYMORPHIC //
// class polymorphic_iarchive :
// public boost::archive::polymorphic_iarchive {
// public:
// void load_override(const char *t, BOOST_PFTO int)
// {
// boost::archive::load_const_override(* this->This(),
// const_cast<char*>(t));
// }
//};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -369,17 +344,13 @@ public:
using base::load_override;
void load_override(const char *str) {
// StringReader sr(basic_text_iprimitive::is);
// sr >> str;
// StringReader sr(basic_text_iprimitive::is);
// sr >> str;
}
~xml_iarchive() {};
virtual ~xml_iarchive() {}
};
// typedef boost::archive::detail::polymorphic_iarchive_route<
// boost::archive::xml_iarchive_impl<xml_iarchive>
//> polymorphic_xml_iarchive;
template <class ArchiveImpl>
struct polymorphic_iarchive_route
: boost::archive::detail::polymorphic_iarchive_route<ArchiveImpl> {
@@ -389,12 +360,8 @@ struct polymorphic_iarchive_route
class polymorphic_xml_iarchive
: public polymorphic_iarchive_route<
boost::archive::xml_iarchive_impl<xml_iarchive>> {
// give serialization implementation access to this class
// friend class boost::archive::detail::interface_iarchive<Archive>;
// friend class boost::archive::basic_xml_iarchive<Archive>;
// friend class boost::archive::load_access;
public:
virtual void load_override(const char *str) { ; }
virtual void load_override(const char *str) {}
};
class xml_oarchive : public boost::archive::xml_oarchive_impl<xml_oarchive> {
@@ -410,11 +377,6 @@ public:
xml_oarchive(std::ostream &os, unsigned int flags = 0)
: boost::archive::xml_oarchive_impl<xml_oarchive>(os, flags) {}
// example of implementing save_override for const char* //
// void save_override(const char *t, int) {
// std::cout << "found char: " << t << "\n";
// }
using basic_xml_oarchive::save_override;
// special treatment for name-value pairs.
@@ -433,10 +395,10 @@ public:
void save_override(const char *str) {
// Do not save any human decoration string //
// basic_text_oprimitive::save(str);
// basic_text_oprimitive::save(str);
}
~xml_oarchive() {}
virtual ~xml_oarchive() {}
};
// typedef boost::archive::detail::polymorphic_oarchive_route<
@@ -471,15 +433,11 @@ public:
sr >> str;
}
~text_iarchive() {};
virtual ~text_iarchive() {}
};
typedef text_iarchive naked_text_iarchive;
// typedef boost::archive::detail::polymorphic_iarchive_route<
// naked_text_iarchive
//> polymorphic_text_iarchive;
class text_oarchive : public boost::archive::text_oarchive_impl<text_oarchive> {
typedef text_oarchive Archive;
typedef boost::archive::text_oarchive_impl<Archive> base;
@@ -497,13 +455,9 @@ public:
void save_override(const char *str) { basic_text_oprimitive::save(str); }
~text_oarchive() {}
virtual ~text_oarchive() {}
};
// typedef boost::archive::detail::polymorphic_oarchive_route<
// boost::archive::text_oarchive_impl<text_oarchive>
//> polymorphic_text_oarchive;
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -540,7 +494,7 @@ public:
sr >> str;
}
~hrt_iarchive() {};
virtual ~hrt_iarchive() {}
};
class hrt_oarchive : public boost::archive::text_oarchive_impl<hrt_oarchive> {
@@ -576,7 +530,7 @@ public:
*this << "\n";
}
~hrt_oarchive() {}
virtual ~hrt_oarchive() {}
};
////////////////////////////////////////////////////////////////////////////////
@@ -605,17 +559,9 @@ public:
}
template <class T> void save_override(const T &t) {
base::save_override(boost::serialization::make_nvp(NULL, t));
base::save_override(boost::serialization::make_nvp(nullptr, t));
}
// activate this if you want to trap non nvp objects //
// template<class T>
// void save_override(T & t)
// {
// BOOST_MPL_ASSERT((boost::serialization::is_wrapper< T >));
// // this->detail_common_oarchive::save_override(t);
// }
template <class T> void save_override(const boost::serialization::nvp<T> &t) {
base::save_override(t);
}
@@ -640,11 +586,9 @@ public:
log_archive(std::ostream &os, unsigned int flags = 0)
: boost::archive::xml_oarchive_impl<log_archive>(
os, flags | boost::archive::no_header) {}
};
// typedef boost::archive::detail::polymorphic_oarchive_route<
// boost::archive::xml_oarchive_impl<log_archive>
//> polymorphic_log_archive;
virtual ~log_archive() {}
};
} // namespace Archive
@@ -658,10 +602,4 @@ ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::hrt_iarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::hrt_oarchive)
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::log_archive)
// ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_xml_iarchive)
// ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_xml_oarchive)
// ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_text_iarchive)
// ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_text_oarchive)
// ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::polymorphic_log_archive)
#endif // U_CORE_ARCHIVES_H

View File

@@ -63,6 +63,13 @@ public:
virtual void serialize(Archive::log_archive & ar, const unsigned int version) = 0;
};
/**
* @brief Template class for typed properties.
*/
@@ -226,7 +233,8 @@ typedef Property<long> LongProperty;
typedef Property<unsigned long> ULongProperty;
typedef Property<float> FloatProperty;
typedef Property<double> DoubleProperty;
typedef Property<Bool_t> BoolProperty;
typedef Property<Bool_t> BoolProperty;
/**
* @brief Property specialized for enumerations, providing labels for GUI representations.
@@ -244,6 +252,16 @@ private:
std::vector<std::string> m_Labels;
};
/**
* @brief Macro to simplify property declaration within a class.
* Usage: ULIB_PROPERTY(float, Width, 1.0f)
@@ -386,12 +404,14 @@ private:
std::vector<std::string> m_GroupStack;
};
/**
* @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); }
{ uLib::Archive::property_register_archive _ar_tmp(&(obj)); _ar_tmp & (obj); }
} // namespace Archive
} // namespace uLib

View File

@@ -440,6 +440,11 @@ using boost::serialization::make_hrp_enum;
template <class ArchiveT> \
void _Ob::save_override(ArchiveT &ar, const unsigned int version)
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////

View File

@@ -23,74 +23,137 @@
//////////////////////////////////////////////////////////////////////////////*/
#include <iostream>
#include <boost/signals2/signal.hpp>
#include <fstream>
#include <string>
#include "Core/Object.h"
#include "Core/Property.h"
#include "Core/Archives.h"
#include "Core/Serializable.h"
#include "testing-prototype.h"
#define emit
template <typename T, bool copyable = true>
class property
{
typedef boost::signals2::signal<void(const property<T>& )> signal_t;
using namespace uLib;
/**
* @brief A test class to demonstrate property registration via SERIALIZE_OBJECT.
*/
class TestObject : public Object {
public:
property() : m_changed(new signal_t) {}
property(const T in) : value(in) , m_changed(new signal_t) {}
uLibTypeMacro(TestObject, Object)
inline operator T const & () const { return value; }
inline operator T & () { return value; }
inline T & operator = (const T &i) { value = i; return value; }
template <typename T2> T2 & operator = (const T2 &i) { T2 &guard = value; } // Assign exact identical types only.
inline signal_t & valueChanged() { return *m_changed; }
TestObject() : m_Value(10.5f), m_Status("Initialized"), m_Counter(0) {}
float m_Value;
std::string m_Status;
int m_Counter;
// Static properties (registered in constructor/initializer)
ULIB_PROPERTY(int, StaticProp, 42)
ULIB_SERIALIZE_ACCESS
template <typename Ar>
void serialize(Ar& ar, unsigned int version) {
ar & HRP("value", m_Value, "mm").range(0, 100).set_default(1.);
ar & HRP("status", m_Status);
ar & HRP("counter", m_Counter);
}
private:
T value;
boost::shared_ptr<signal_t> m_changed;
};
//template <typename T>
//class property <T,false> {
// typedef boost::signals2::signal<void( T )> signal_t;
class TestObject2 : public TestObject {
public:
uLibTypeMacro(TestObject2, TestObject)
//public:
// property() : m_changed() {}
// property(const T in) : value(in) , m_changed() {}
TestObject2() : TestObject(), m_Value2(20.5f) {}
// inline operator T const & () const { return value; }
// inline operator T & () { valueChanged()(value); return value; }
// inline T & operator = (const T &i) { value = i; valueChanged()(value); return value; }
// template <typename T2> T2 & operator = (const T2 &i) { T2 &guard = value; } // Assign exact identical types only.
// inline signal_t &valueChanged() { return m_changed; }
//private:
// property(const property<T> &);
// property<T> &operator = (const property<T>&);
// T value;
// signal_t m_changed;
//};
// test generic void function slot //
void PrintSlot(const property<int> &i) { std::cout << "slot called, new value = " << i << "!\n"; }
int main()
{
float m_Value2;
ULIB_SERIALIZE_ACCESS
};
ULIB_SERIALIZABLE_OBJECT(TestObject2)
ULIB_SERIALIZE_OBJECT(TestObject2, TestObject) {
// std::cout << "Serializing TestObject2" << std::endl;
ar & boost::serialization::make_hrp("value2", ob.m_Value2, "mm").set_default(1.);
}
int main() {
BEGIN_TESTING(Properties Serialization)
TestObject obj;
// 1. Initial state: check static property
ASSERT_EQUAL(obj.StaticProp, 42);
// 2. Activate dynamic properties via the property_register_archive
// This calls the serialize method with a special archive that populates m_DynamicProperties
ULIB_ACTIVATE_PROPERTIES(obj);
const auto& props = obj.GetProperties();
// This is problematic because GetProperties currently returns d->m_Properties (only static)
// For now, let's just assert on the dynamic property presence if possible
PropertyBase* pVal = obj.GetProperty("value");
ASSERT_NOT_NULL(pVal);
ASSERT_EQUAL(pVal->GetValueAsString(), "10.5");
ASSERT_EQUAL(pVal->GetUnits(), "mm");
// Check other dynamic properties
ASSERT_NOT_NULL(obj.GetProperty("status"));
ASSERT_NOT_NULL(obj.GetProperty("counter"));
// 4. Serialization round-trip (XML)
{
std::ofstream ofs("test_props.xml");
Archive::xml_oarchive(ofs) << NVP("test_obj", obj);
}
TestObject obj2;
obj2.m_Value = 0;
obj2.m_Status = "";
{
std::ifstream ifs("test_props.xml");
Archive::xml_iarchive(ifs) >> NVP("test_obj", obj2);
}
ASSERT_EQUAL(obj2.m_Value, 10.5f);
ASSERT_EQUAL(obj2.m_Status, "Initialized");
TestObject2 obj3;
obj3.m_Value = 12.5;
obj3.m_Status = "Initialized";
obj3.m_Value2 = 22.5;
ULIB_ACTIVATE_PROPERTIES(obj3);
PropertyBase* pVal3 = obj3.GetProperty("value2");
ASSERT_NOT_NULL(pVal3);
ASSERT_EQUAL(pVal3->GetValueAsString(), "22.5");
ASSERT_EQUAL(pVal3->GetUnits(), "mm");
// 5. Serialization round-trip (XML)
{
std::ofstream ofs("test_props2.xml");
Archive::xml_oarchive(ofs) << NVP("test_obj2", obj3);
}
TestObject2 obj4;
obj4.m_Value = 0;
obj4.m_Status = "";
obj4.m_Value2 = 0;
ULIB_ACTIVATE_PROPERTIES(obj4);
{
std::ifstream ifs("test_props2.xml");
Archive::xml_iarchive(ifs) >> NVP("test_obj2", obj4);
}
ASSERT_EQUAL(obj4.m_Value, 12.5f);
ASSERT_EQUAL(obj4.m_Status, "Initialized");
ASSERT_EQUAL(obj4.m_Value2, 22.5f);
END_TESTING
}

View File

@@ -40,7 +40,7 @@ struct A : Object {
};
ULIB_SERIALIZABLE_OBJECT(A)
ULIB_SERIALIZE_OBJECT(A, Object) { ar &AR(numa); }
ULIB_SERIALIZE_OBJECT(A, Object) { ar & AR(numa); }
struct B : virtual Object {
uLibTypeMacro(B, Object) B() : numb(5552369) {}
@@ -48,7 +48,7 @@ struct B : virtual Object {
};
ULIB_SERIALIZABLE_OBJECT(B)
ULIB_SERIALIZE_OBJECT(B, Object) { ar &AR(numb); }
ULIB_SERIALIZE_OBJECT(B, Object) { ar & AR(numb); }
struct C : B {
uLibTypeMacro(C, B) C() : numc(5552370) {}
@@ -56,7 +56,7 @@ struct C : B {
};
ULIB_SERIALIZABLE_OBJECT(C)
ULIB_SERIALIZE_OBJECT(C, B) { ar &AR(numc); }
ULIB_SERIALIZE_OBJECT(C, B) { ar & AR(numc); }
struct D : A, B {
uLibTypeMacro(D, A, B)
@@ -67,10 +67,33 @@ struct D : A, B {
};
ULIB_SERIALIZABLE_OBJECT(D)
ULIB_SERIALIZE_OBJECT(D, A, B) { ar &AR(numd); }
ULIB_SERIALIZE_OBJECT(D, A, B) { ar & AR(numd); }
int main() {
A o;
Archive::xml_oarchive(std::cout) << NVP(o);
BEGIN_TESTING(DreadDiamond Serialization)
D o;
C c;
c.numb = 123;
{
std::ofstream file("test.xml");
Archive::xml_oarchive(file) << NVP("dd_test", o) << NVP("c", c);
}
{
D o2;
C c2;
std::ifstream file("test.xml");
Archive::xml_iarchive(file) >> NVP("dd_test", o2) >> NVP("c", c2);
// D //
ASSERT_EQUAL(o.numa, o2.numa);
ASSERT_EQUAL(o.numb, o2.numb);
ASSERT_EQUAL(o.numd, o2.numd);
// C //
ASSERT_EQUAL(c.numb, c2.numb);
ASSERT_EQUAL(c.numc, c2.numc);
}
END_TESTING
}

View File

@@ -143,22 +143,19 @@ int testing_hrt_class() {
}
a.a() = 0;
a.p_a = "zero string";
{
// ERRORE FIX !
// std::ifstream file("test.xml");
// Archive::hrt_iarchive(file) >> NVP(a);
}
Archive::hrt_oarchive(std::cout) << NVP(a);
return (a.a() == 5552368 && a.p_a == "A property string");
}
int main() {
BEGIN_TESTING(Serialize Test);
TEST1(test_V3f());
TEST1(testing_xml_class());
// testing_hrt_class(); ///// << ERRORE in HRT with properties
// TEST1(testing_hrt_class());
END_TESTING;
}