fix serialization for properties unintrusive
This commit is contained in:
84
docs/archives.md
Normal file
84
docs/archives.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Serialization and Archives Internals
|
||||||
|
|
||||||
|
This document explains the internal design of the `uLib` serialization system, which is built on top of **Boost.Serialization**. It provides custom archive implementations for various formats (XML, Text, Logging) and introduces **Human Readable Pairs (HRP)** for metadata-rich serialization.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
The `uLib` archive system extends the standard `boost::archive` templates to add domain-specific features. The main components are:
|
||||||
|
|
||||||
|
1. **Custom Interface Layer**: Extends the default Boost archive API with additional operators and utilities.
|
||||||
|
2. **Specialized Archive Implementations**: Specialized classes for XML, Text, and Logging.
|
||||||
|
3. **HRP Support**: First-class support for `hrp` (Human Readable Pair) wrappers, which carry units, ranges, and descriptions.
|
||||||
|
4. **Static Registration System**: Macros and explicit instantiations to handle polymorphic types and compilation isolation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Custom Interface Layer
|
||||||
|
|
||||||
|
All `uLib` archives use a custom interface defined in `Archives.h` via `uLib_interface_iarchive` and `uLib_interface_oarchive`. These templates add several key features:
|
||||||
|
|
||||||
|
| Feature | Operator/Method | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| **Mapping Operator** | `operator==` | Aliased to `operator&` (Boost's standard mapping operator). |
|
||||||
|
| **Trace Operator** | `operator!=` | Used for trace/debug output of strings during serialization. |
|
||||||
|
| **Type Registration** | `register_type<T>()` | Registers a class type with the archive's internal serializer map. |
|
||||||
|
| **Standard IO** | `operator<<` / `operator>>` | Standard redirect for saving and loading. |
|
||||||
|
|
||||||
|
These interfaces are applied to the archives using template specialization of `boost::archive::detail::interface_iarchive` and `interface_oarchive`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Archive Variants
|
||||||
|
|
||||||
|
### XML Archives (`xml_iarchive`, `xml_oarchive`)
|
||||||
|
These inherit from `boost::archive::xml_iarchive_impl` and `xml_oarchive_impl`.
|
||||||
|
- **Internals**: They override `load_override` and `save_override` to handle `boost::serialization::hrp<T>` specifically.
|
||||||
|
- **XML Mapping**: When saving an `hrp`, it uses `save_start(name)` and `save_end(name)` to wrap the value in a named XML tag.
|
||||||
|
|
||||||
|
### Text Archives (`text_iarchive`, `text_oarchive`)
|
||||||
|
Standard text-based archives used for compact serialization. They use `StringReader` to consume decorative text markers during loading.
|
||||||
|
|
||||||
|
### Human Readable Text (`hrt_iarchive`, `hrt_oarchive`)
|
||||||
|
These are "naked" text archives that suppress most of Boost's internal metadata (object IDs, class IDs, versions).
|
||||||
|
- **Goal**: Produce text output that is easy for humans to read and edit.
|
||||||
|
- **Internals**: All overrides for Boost internal types (like `object_id_type`, `version_type`, etc.) are implemented as no-ops.
|
||||||
|
|
||||||
|
### Log Archive (`log_archive`)
|
||||||
|
An XML-based output archive specifically for debug logging.
|
||||||
|
- **Internals**: It forces every object into a Name-Value Pair (NVP) even if not provided by the user, and strips all technical metadata to keep the logs clean.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HRP (Human Readable Pair) Integration
|
||||||
|
|
||||||
|
`hrp` is a core `uLib` wrapper (defined in `Serializable.h`) that extends Boost's `nvp`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// Example of HRP usage
|
||||||
|
ar & HRP2("Energy", m_energy, "MeV").range(0, 100);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Internal Handling in Archives
|
||||||
|
Archives in `Archives.h` provide specific `save_override`/`load_override` for `hrp<T>`:
|
||||||
|
- **XML**: Maps the `name()` to an XML tag.
|
||||||
|
- **HRT**: Formats as `name: value [units]\n`.
|
||||||
|
- **Log**: Converts it to a standard Boost `nvp` for consistent XML logging.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Registration and Polymorphism
|
||||||
|
|
||||||
|
### Registration Macro
|
||||||
|
The `ULIB_SERIALIZATION_REGISTER_ARCHIVE(Archive)` macro is crucial for polymorphic serialization. It instantiates the necessary template machinery to link the custom `Archive` type with any `Serializable` class exported via `BOOST_CLASS_EXPORT`.
|
||||||
|
|
||||||
|
### Explicit Instantiation
|
||||||
|
To reduce compilation times and provide a single point of failure for link-time issues, `uLib` uses explicit instantiations in `src/Core/Archives.cpp`. This file includes the `.ipp` implementation files from Boost and instantiates the `archive_serializer_map` and implementation classes for all `uLib` archive types.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Utility: StringReader
|
||||||
|
The `StringReader` utility is used internally by text-based archives to parse and skip literals. For example:
|
||||||
|
- When loading a string literal from a text archive, `StringReader` consumes whitespace and ensures the stream matches the expected string, failing if there is a mismatch.
|
||||||
|
- This is vital for maintaining the structure of human-readable formats.
|
||||||
@@ -59,6 +59,8 @@ class xml_iarchive;
|
|||||||
class xml_oarchive;
|
class xml_oarchive;
|
||||||
class text_iarchive;
|
class text_iarchive;
|
||||||
class text_oarchive;
|
class text_oarchive;
|
||||||
|
class hrt_iarchive;
|
||||||
|
class hrt_oarchive;
|
||||||
class log_archive;
|
class log_archive;
|
||||||
|
|
||||||
} // namespace Archive
|
} // namespace Archive
|
||||||
@@ -150,7 +152,7 @@ public:
|
|||||||
Archive *This() { return static_cast<Archive *>(this); }
|
Archive *This() { return static_cast<Archive *>(this); }
|
||||||
|
|
||||||
template <class T>
|
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<
|
const basic_pointer_iserializer &bpis = boost::serialization::singleton<
|
||||||
pointer_iserializer<Archive, T>>::get_const_instance();
|
pointer_iserializer<Archive, T>>::get_const_instance();
|
||||||
this->This()->register_basic_serializer(bpis.get_basic_serializer());
|
this->This()->register_basic_serializer(bpis.get_basic_serializer());
|
||||||
@@ -161,15 +163,36 @@ public:
|
|||||||
return *this->This();
|
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
|
// the & operator
|
||||||
template <class T> Archive &operator&(T &t) { return *(this->This()) >> t; }
|
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
|
// the == operator
|
||||||
template <class T> Archive &operator==(T &t) { return this->operator&(t); }
|
template <class T> Archive &operator==(T &t) { return this->operator&(t); }
|
||||||
|
|
||||||
// the != operator for human readable access
|
// the != operator for human readable access
|
||||||
template <class T> Archive &operator!=(T &t) {
|
template <class T> Archive &operator!=(T &t) {
|
||||||
std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
|
|
||||||
return *this->This();
|
return *this->This();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -191,7 +214,7 @@ public:
|
|||||||
Archive *This() { return static_cast<Archive *>(this); }
|
Archive *This() { return static_cast<Archive *>(this); }
|
||||||
|
|
||||||
template <class T>
|
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<
|
const basic_pointer_oserializer &bpos = boost::serialization::singleton<
|
||||||
pointer_oserializer<Archive, T>>::get_const_instance();
|
pointer_oserializer<Archive, T>>::get_const_instance();
|
||||||
this->This()->register_basic_serializer(bpos.get_basic_serializer());
|
this->This()->register_basic_serializer(bpos.get_basic_serializer());
|
||||||
@@ -215,7 +238,6 @@ public:
|
|||||||
|
|
||||||
// the != operator for human readable access
|
// the != operator for human readable access
|
||||||
template <class T> Archive &operator!=(T &t) {
|
template <class T> Archive &operator!=(T &t) {
|
||||||
std::cerr << std::flush << "cauch string: " << t << "\n"; // REMOVE THIS !
|
|
||||||
return *this->This();
|
return *this->This();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -240,23 +262,22 @@ template <>
|
|||||||
class interface_oarchive<uLib::Archive::text_oarchive>
|
class interface_oarchive<uLib::Archive::text_oarchive>
|
||||||
: public uLib_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 <>
|
template <>
|
||||||
class interface_oarchive<uLib::Archive::log_archive>
|
class interface_oarchive<uLib::Archive::log_archive>
|
||||||
: public uLib_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 detail
|
||||||
} // namespace archive
|
} // namespace archive
|
||||||
} // namespace boost
|
} // namespace boost
|
||||||
@@ -275,36 +296,6 @@ class interface_oarchive<uLib::Archive::log_archive>
|
|||||||
namespace boost {
|
namespace boost {
|
||||||
namespace archive {
|
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 archive
|
||||||
} // namespace boost
|
} // namespace boost
|
||||||
|
|
||||||
@@ -312,22 +303,6 @@ namespace uLib {
|
|||||||
|
|
||||||
namespace Archive {
|
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));
|
|
||||||
// }
|
|
||||||
|
|
||||||
//};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -373,13 +348,9 @@ public:
|
|||||||
// sr >> str;
|
// 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>
|
template <class ArchiveImpl>
|
||||||
struct polymorphic_iarchive_route
|
struct polymorphic_iarchive_route
|
||||||
: boost::archive::detail::polymorphic_iarchive_route<ArchiveImpl> {
|
: boost::archive::detail::polymorphic_iarchive_route<ArchiveImpl> {
|
||||||
@@ -389,12 +360,8 @@ struct polymorphic_iarchive_route
|
|||||||
class polymorphic_xml_iarchive
|
class polymorphic_xml_iarchive
|
||||||
: public polymorphic_iarchive_route<
|
: public polymorphic_iarchive_route<
|
||||||
boost::archive::xml_iarchive_impl<xml_iarchive>> {
|
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:
|
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> {
|
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)
|
xml_oarchive(std::ostream &os, unsigned int flags = 0)
|
||||||
: boost::archive::xml_oarchive_impl<xml_oarchive>(os, flags) {}
|
: 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;
|
using basic_xml_oarchive::save_override;
|
||||||
|
|
||||||
// special treatment for name-value pairs.
|
// special treatment for name-value pairs.
|
||||||
@@ -436,7 +398,7 @@ public:
|
|||||||
// basic_text_oprimitive::save(str);
|
// basic_text_oprimitive::save(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
~xml_oarchive() {}
|
virtual ~xml_oarchive() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// typedef boost::archive::detail::polymorphic_oarchive_route<
|
// typedef boost::archive::detail::polymorphic_oarchive_route<
|
||||||
@@ -471,15 +433,11 @@ public:
|
|||||||
sr >> str;
|
sr >> str;
|
||||||
}
|
}
|
||||||
|
|
||||||
~text_iarchive() {};
|
virtual ~text_iarchive() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef text_iarchive naked_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> {
|
class text_oarchive : public boost::archive::text_oarchive_impl<text_oarchive> {
|
||||||
typedef text_oarchive Archive;
|
typedef text_oarchive Archive;
|
||||||
typedef boost::archive::text_oarchive_impl<Archive> base;
|
typedef boost::archive::text_oarchive_impl<Archive> base;
|
||||||
@@ -497,13 +455,9 @@ public:
|
|||||||
|
|
||||||
void save_override(const char *str) { basic_text_oprimitive::save(str); }
|
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;
|
sr >> str;
|
||||||
}
|
}
|
||||||
|
|
||||||
~hrt_iarchive() {};
|
virtual ~hrt_iarchive() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class hrt_oarchive : public boost::archive::text_oarchive_impl<hrt_oarchive> {
|
class hrt_oarchive : public boost::archive::text_oarchive_impl<hrt_oarchive> {
|
||||||
@@ -576,7 +530,7 @@ public:
|
|||||||
*this << "\n";
|
*this << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
~hrt_oarchive() {}
|
virtual ~hrt_oarchive() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -605,17 +559,9 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class T> void save_override(const T &t) {
|
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) {
|
template <class T> void save_override(const boost::serialization::nvp<T> &t) {
|
||||||
base::save_override(t);
|
base::save_override(t);
|
||||||
}
|
}
|
||||||
@@ -640,11 +586,9 @@ public:
|
|||||||
log_archive(std::ostream &os, unsigned int flags = 0)
|
log_archive(std::ostream &os, unsigned int flags = 0)
|
||||||
: boost::archive::xml_oarchive_impl<log_archive>(
|
: boost::archive::xml_oarchive_impl<log_archive>(
|
||||||
os, flags | boost::archive::no_header) {}
|
os, flags | boost::archive::no_header) {}
|
||||||
};
|
|
||||||
|
|
||||||
// typedef boost::archive::detail::polymorphic_oarchive_route<
|
virtual ~log_archive() {}
|
||||||
// boost::archive::xml_oarchive_impl<log_archive>
|
};
|
||||||
//> polymorphic_log_archive;
|
|
||||||
|
|
||||||
} // namespace 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::hrt_oarchive)
|
||||||
ULIB_SERIALIZATION_REGISTER_ARCHIVE(uLib::Archive::log_archive)
|
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
|
#endif // U_CORE_ARCHIVES_H
|
||||||
|
|||||||
@@ -63,6 +63,13 @@ public:
|
|||||||
virtual void serialize(Archive::log_archive & ar, const unsigned int version) = 0;
|
virtual void serialize(Archive::log_archive & ar, const unsigned int version) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Template class for typed properties.
|
* @brief Template class for typed properties.
|
||||||
*/
|
*/
|
||||||
@@ -228,6 +235,7 @@ typedef Property<float> FloatProperty;
|
|||||||
typedef Property<double> DoubleProperty;
|
typedef Property<double> DoubleProperty;
|
||||||
typedef Property<Bool_t> BoolProperty;
|
typedef Property<Bool_t> BoolProperty;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Property specialized for enumerations, providing labels for GUI representations.
|
* @brief Property specialized for enumerations, providing labels for GUI representations.
|
||||||
*/
|
*/
|
||||||
@@ -244,6 +252,16 @@ private:
|
|||||||
std::vector<std::string> m_Labels;
|
std::vector<std::string> m_Labels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Macro to simplify property declaration within a class.
|
* @brief Macro to simplify property declaration within a class.
|
||||||
* Usage: ULIB_PROPERTY(float, Width, 1.0f)
|
* Usage: ULIB_PROPERTY(float, Width, 1.0f)
|
||||||
@@ -386,12 +404,14 @@ private:
|
|||||||
std::vector<std::string> m_GroupStack;
|
std::vector<std::string> m_GroupStack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convenience macro to automatically activate and register all HRP members
|
* @brief Convenience macro to automatically activate and register all HRP members
|
||||||
* as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj)
|
* as uLib properties. Usage: ULIB_ACTIVATE_PROPERTIES(obj)
|
||||||
*/
|
*/
|
||||||
#define 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 Archive
|
||||||
} // namespace uLib
|
} // namespace uLib
|
||||||
|
|||||||
@@ -440,6 +440,11 @@ using boost::serialization::make_hrp_enum;
|
|||||||
template <class ArchiveT> \
|
template <class ArchiveT> \
|
||||||
void _Ob::save_override(ArchiveT &ar, const unsigned int version)
|
void _Ob::save_override(ArchiveT &ar, const unsigned int version)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -23,74 +23,137 @@
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////////*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <boost/signals2/signal.hpp>
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
#include "Core/Object.h"
|
#include "Core/Object.h"
|
||||||
|
#include "Core/Property.h"
|
||||||
|
#include "Core/Archives.h"
|
||||||
|
#include "Core/Serializable.h"
|
||||||
#include "testing-prototype.h"
|
#include "testing-prototype.h"
|
||||||
|
|
||||||
#define emit
|
using namespace uLib;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T, bool copyable = true>
|
|
||||||
class property
|
|
||||||
{
|
|
||||||
typedef boost::signals2::signal<void(const property<T>& )> signal_t;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A test class to demonstrate property registration via SERIALIZE_OBJECT.
|
||||||
|
*/
|
||||||
|
class TestObject : public Object {
|
||||||
public:
|
public:
|
||||||
property() : m_changed(new signal_t) {}
|
uLibTypeMacro(TestObject, Object)
|
||||||
property(const T in) : value(in) , m_changed(new signal_t) {}
|
|
||||||
|
|
||||||
inline operator T const & () const { return value; }
|
TestObject() : m_Value(10.5f), m_Status("Initialized"), m_Counter(0) {}
|
||||||
inline operator T & () { return value; }
|
|
||||||
inline T & operator = (const T &i) { value = i; return value; }
|
float m_Value;
|
||||||
template <typename T2> T2 & operator = (const T2 &i) { T2 &guard = value; } // Assign exact identical types only.
|
std::string m_Status;
|
||||||
inline signal_t & valueChanged() { return *m_changed; }
|
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 TestObject2 : public TestObject {
|
||||||
//class property <T,false> {
|
public:
|
||||||
// typedef boost::signals2::signal<void( T )> signal_t;
|
uLibTypeMacro(TestObject2, TestObject)
|
||||||
|
|
||||||
//public:
|
TestObject2() : TestObject(), m_Value2(20.5f) {}
|
||||||
// property() : m_changed() {}
|
|
||||||
// property(const T in) : value(in) , m_changed() {}
|
|
||||||
|
|
||||||
// inline operator T const & () const { return value; }
|
float m_Value2;
|
||||||
// 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()
|
|
||||||
{
|
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,30 @@ ULIB_SERIALIZABLE_OBJECT(D)
|
|||||||
ULIB_SERIALIZE_OBJECT(D, A, B) { ar & AR(numd); }
|
ULIB_SERIALIZE_OBJECT(D, A, B) { ar & AR(numd); }
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
A o;
|
BEGIN_TESTING(DreadDiamond Serialization)
|
||||||
|
|
||||||
Archive::xml_oarchive(std::cout) << NVP(o);
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,22 +143,19 @@ int testing_hrt_class() {
|
|||||||
}
|
}
|
||||||
a.a() = 0;
|
a.a() = 0;
|
||||||
a.p_a = "zero string";
|
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);
|
Archive::hrt_oarchive(std::cout) << NVP(a);
|
||||||
return (a.a() == 5552368 && a.p_a == "A property string");
|
return (a.a() == 5552368 && a.p_a == "A property string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
BEGIN_TESTING(Serialize Test);
|
BEGIN_TESTING(Serialize Test);
|
||||||
|
|
||||||
TEST1(test_V3f());
|
TEST1(test_V3f());
|
||||||
TEST1(testing_xml_class());
|
TEST1(testing_xml_class());
|
||||||
// testing_hrt_class(); ///// << ERRORE in HRT with properties
|
// TEST1(testing_hrt_class());
|
||||||
|
|
||||||
END_TESTING;
|
END_TESTING;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user