Commit 449e4a0a authored by Joseph Mirabel's avatar Joseph Mirabel
Browse files

[Serialization] Add pointer holder in archive.

parent 26ca869c
Pipeline #12671 passed with stage
in 4 minutes and 38 seconds
...@@ -65,7 +65,7 @@ SET(${PROJECT_NAME}_SOURCES ...@@ -65,7 +65,7 @@ SET(${PROJECT_NAME}_SOURCES
ADD_LIBRARY(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS}) ADD_LIBRARY(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PRIVATE ${TinyXML_INCLUDE_DIR}) TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PRIVATE ${TinyXML_INCLUDE_DIR})
TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:include>) TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:include>)
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${TinyXML_LIBRARY} Boost::filesystem Boost::serialization) TARGET_LINK_LIBRARIES(${PROJECT_NAME} PUBLIC ${TinyXML_LIBRARY} Boost::filesystem Boost::serialization)
# Check for unistd.h presence. # Check for unistd.h presence.
INCLUDE(CheckIncludeFiles) INCLUDE(CheckIncludeFiles)
......
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
#include <boost/serialization/export.hpp> #include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/nvp.hpp> #include <boost/serialization/nvp.hpp>
#include <boost/serialization/utility.hpp>
#include <hpp/util/config.hh>
#include <hpp/util/serialization-fwd.hh>
#define _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type,archive,arg) \ #define _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type,archive,arg) \
template void type load<archive##_iarchive>(archive##_iarchive& ar, \ template void type load<archive##_iarchive>(archive##_iarchive& ar, \
...@@ -47,7 +51,6 @@ ...@@ -47,7 +51,6 @@
arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \ arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
const unsigned int ver) const unsigned int ver)
#if BOOST_VERSION >= 106500
#define HPP_SERIALIZATION_SPLIT_IMPLEMENT(type) \ #define HPP_SERIALIZATION_SPLIT_IMPLEMENT(type) \
_HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::xml,); \ _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::xml,); \
_HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::binary,) _HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::binary,)
...@@ -72,37 +75,143 @@ ...@@ -72,37 +75,143 @@
_HPP_SERIALIZATION_IMPLEMENT(,archive::binary,type& t); \ _HPP_SERIALIZATION_IMPLEMENT(,archive::binary,type& t); \
}} }}
#else // BOOST_VERSION >= 106500
#define HPP_SERIALIZATION_SPLIT_IMPLEMENT(type) \
_HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::xml,); \
_HPP_SERIALIZATION_SPLIT_IMPLEMENT(type::,boost::archive::binary,)
#define HPP_SERIALIZATION_IMPLEMENT(type) \
_HPP_SERIALIZATION_IMPLEMENT(type::,boost::archive::xml,); \
_HPP_SERIALIZATION_IMPLEMENT(type::,boost::archive::binary,)
#define HPP_SERIALIZATION_FREE_IMPLEMENT(type) \
namespace boost { namespace serialization { \
_HPP_SERIALIZATION_IMPLEMENT(,archive::xml,type& t); \
_HPP_SERIALIZATION_IMPLEMENT(,archive::binary,type& t); \
}}
#define HPP_SERIALIZATION_SPLIT_FREE_IMPLEMENT(type) \
namespace boost { namespace serialization { \
template<class Archive> \
void serialize(Archive & ar, type& t, const unsigned int version) { \
split_free(ar, t, version); \
} \
_HPP_SERIALIZATION_IMPLEMENT(,archive::xml,type& t); \
_HPP_SERIALIZATION_IMPLEMENT(,archive::binary,type& t); \
}}
#endif // BOOST_VERSION >= 106500
namespace hpp { namespace hpp {
namespace serialization { namespace serialization {
using boost::serialization::make_nvp; using boost::serialization::make_nvp;
using boost::serialization::guid;
using boost::serialization::guid_defined;
class holder_base {
public:
const char* classid;
virtual ~holder_base() = default;
protected:
holder_base(const char* classid) : classid(classid) {}
};
template<typename T> class holder : public holder_base {
public:
T* t;
holder(T* t, const char* classid) : holder_base(classid), t(t) {}
holder(T* t) : holder(t, guid<T>())
{ static_assert(guid_defined<T>::value, "You must use BOOST_CLASS_EXPORT_KEY on this class first."); }
template <typename U> explicit operator holder<U>() { return holder<U>(dynamic_cast<U*>(t), classid); }
};
class archive_ptr_holder
{
std::map<std::string, holder_base*> ptrs_;
public:
bool contains(const std::string& k) const { return ptrs_.count(k); }
template<typename T>
bool containsOfType(const std::string& k) const { return (get<T>(k, false)) != NULL; }
void insert(const std::string& k, holder_base* ptr) { ptrs_[k] = ptr; }
template<typename T>
void insert(const std::string& k, T* ptr) { insert(k, (holder_base*)new holder<T>(ptr)); }
holder_base* get(const std::string& k, bool throwIfNotFound = false) const {
auto _ptr = ptrs_.find(k);
if (_ptr == ptrs_.end()) {
if (!throwIfNotFound) return NULL;
throw std::invalid_argument("Pointer with name " + k + " not found.");
}
else return _ptr->second;
}
template<typename T>
T* get(const std::string& k, bool throwIfNotFound = false) const {
holder_base* hb (get(k, throwIfNotFound));
if (hb == NULL) return NULL;
holder<T>* h;
if ((h = dynamic_cast<holder<T>*>(hb)) == NULL) {
if (!throwIfNotFound) return NULL;
throw std::invalid_argument("Pointer with name " + k + " found of type "
+ hb->classid + " but not of requested type " + guid<T>() + ".");
}
return h->t;
}
template<typename Base, typename Child>
void insertChildClass(const std::string& k, Child* ptr) {
static_assert(guid_defined<Child>::value, "You must use BOOST_CLASS_EXPORT_KEY on this class first.");
insert(k, (holder_base*)new holder<Base>(ptr, guid<Child>()));
}
template<typename Base, typename Child>
Child* getChildClass(const std::string& k, bool throwIfNotFound = false) const {
holder_base* hb (get(k, throwIfNotFound));
if (hb == NULL) return NULL;
holder<Base>* b;
if ((b = dynamic_cast<holder<Base>*>(hb)) == NULL) {
if (!throwIfNotFound) return NULL;
throw std::invalid_argument("Pointer with name " + k + " found of type "
+ hb->classid + " but not of requested type " + guid<Child>() + ".");
}
holder<Child> c (*b);
if (c.t == NULL) {
if (!throwIfNotFound) return NULL;
throw std::invalid_argument("Pointer with name " + k + " found of type "
+ hb->classid + " but not of requested type " + guid<Child>() + ".");
}
return c.t;
}
virtual ~archive_ptr_holder() {
for (auto it : ptrs_) delete it.second;
}
protected:
template<typename Archive, std::enable_if_t<Archive::is_saving::value, int> = 42>
void initialize_tpl(Archive& ar) // save
{
auto size (ptrs_.size());
ar << make_nvp("nrequires", size);
typedef std::pair<std::string,std::string> string_pair;
for (auto it : ptrs_) {
string_pair requires(it.first, it.second->classid);
ar << make_nvp("requires", requires);
}
}
template<typename Archive, std::enable_if_t<!Archive::is_saving::value, int> = 42>
void initialize_tpl(Archive& ar) // read
{
decltype(ptrs_.size()) size;
ar >> make_nvp("nrequires", size);
typedef std::pair<std::string,std::string> string_pair;
string_pair pair;
for (decltype(size) i = 0; i < size; ++i) {
ar >> make_nvp("requires", pair);
holder_base* hb = get(pair.first, true);
if (pair.second != hb->classid)
throw std::invalid_argument("Required pointer with name " + pair.first + " found of type "
+ hb->classid + " but not of required type " + pair.second + ".");
}
}
};
template<typename archive_base>
class archive_tpl : public archive_base, public archive_ptr_holder
{
public:
inline void initialize()
{
initialize_tpl<archive_base>(*this);
}
using archive_base::archive_base;
};
template<typename Archive>
archive_tpl<Archive>& cast(Archive& ar) { return dynamic_cast<archive_tpl<Archive>&>(ar); }
template<typename Archive>
archive_tpl<Archive>* cast(Archive* ar) { return dynamic_cast<archive_tpl<Archive>*>(ar); }
typedef archive_tpl<boost::archive::binary_iarchive> binary_iarchive;
typedef archive_tpl<boost::archive::binary_oarchive> binary_oarchive;
typedef archive_tpl<boost::archive::xml_iarchive> xml_iarchive;
typedef archive_tpl<boost::archive::xml_oarchive> xml_oarchive;
} // namespace util } // namespace util
} // namespace hpp } // namespace hpp
......
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
# include <iostream> # include <iostream>
# include "config.h" # include "config.h"
static const int TEST_FAILED = 10; static constexpr int TEST_FAILED = 10;
static const int TEST_SUCCEED = 0; static constexpr int TEST_SUCCEED = 0;
# define GENERATE_TEST() \ # define GENERATE_TEST() \
int \ int \
......
...@@ -20,34 +20,113 @@ ...@@ -20,34 +20,113 @@
#include "common.hh" #include "common.hh"
#include "serialization.hh" #include "serialization.hh"
#include <boost/archive/xml_iarchive.hpp> #include <hpp/util/serialization.hh>
#include <boost/archive/xml_oarchive.hpp>
template<typename T> template<typename T>
int run_test_tpl () int run_test_tpl ()
{ {
std::stringstream ss; std::stringstream ss;
T t(10); T* t(new T(10));
{ {
boost::archive::xml_oarchive oa(ss); hpp::serialization::xml_oarchive oa(ss);
oa << boost::serialization::make_nvp("t", t); oa << boost::serialization::make_nvp("t", t);
} }
Foo* t_r = NULL; std::cout << ss.str() << std::endl;
T* t_r = NULL;
{ {
boost::archive::xml_iarchive ia(ss); hpp::serialization::xml_iarchive ia(ss);
ia >> boost::serialization::make_nvp("t", t_r); ia >> boost::serialization::make_nvp("t", t_r);
} }
if (t_r == NULL || t_r->i_ != t.i_) return TEST_FAILED; if (t_r == NULL || t_r->i_ != t->i_) {
std::cerr << "Failed to deserialize " << hpp::serialization::guid<T>() << " class" << std::endl;
return TEST_FAILED;
}
delete t;
return TEST_SUCCEED;
}
int run_test_bar (bool check_throw)
{
std::unique_ptr<Foo> foo (new Foo(1));
std::stringstream ss;
Bar* t(new Bar(10));
{
hpp::serialization::xml_oarchive oa(ss);
oa.insert("foo", foo.get());
oa.initialize();
oa << boost::serialization::make_nvp("t", t);
}
std::cout << ss.str() << std::endl;
Bar* t_r = NULL;
bool caught_error;
{
hpp::serialization::xml_iarchive ia(ss);
if (!check_throw)
ia.insert("foo", foo.get());
else
ia.insert("foo", new Bar(0));
try {
caught_error=true;
ia.initialize();
caught_error=false;
ia >> boost::serialization::make_nvp("t", t_r);
} catch (const std::invalid_argument& e) {
std::cerr << e.what() << std::endl;
}
}
if (check_throw != caught_error) return TEST_FAILED;
if (check_throw && t_r != NULL) {
std::cerr << "Deserialize Bar class while it shouldn't" << std::endl;
return TEST_FAILED;
}
if (!check_throw) {
if (t_r->f_ != foo.get()) {
std::cerr << "Failed to deserialize Bar::f_ Foo pointer" << std::endl;
return TEST_FAILED;
}
if (t_r == NULL || t_r->i_ != t->i_) {
std::cerr << "Failed to deserialize Bar class" << std::endl;
return TEST_FAILED;
}
}
delete t;
return TEST_SUCCEED;
}
struct S1 { virtual ~S1() = default; };
struct S2 : S1 {};
BOOST_CLASS_EXPORT_KEY(S1)
BOOST_CLASS_EXPORT_KEY(S2)
int run_test_holder_inheritance()
{
using namespace hpp::serialization;
archive_ptr_holder aph;
S2 s2;
aph.insert<S1>("s1", &s2);
aph.insertChildClass<S1>("s2", &s2);
aph.template get<S1>("s1", true);
aph.getChildClass<S1, S2>("s2", true);
S1* s1 = aph.template get<S1>("s2", false);
if (s1 == NULL) {
std::cerr << "Failed to cast S2 into S1" << std::endl;
return TEST_FAILED;
}
return TEST_SUCCEED; return TEST_SUCCEED;
} }
int run_test () int run_test ()
{ {
if (run_test_tpl<Foo >() == TEST_FAILED) return TEST_FAILED; if (run_test_tpl<Foo >() == TEST_FAILED) return TEST_FAILED;
if (run_test_tpl<Bar >() == TEST_FAILED) return TEST_FAILED; if (run_test_bar (true) == TEST_FAILED) return TEST_FAILED;
if (run_test_bar (false) == TEST_FAILED) return TEST_FAILED;
if (run_test_tpl<FooFree>() == TEST_FAILED) return TEST_FAILED; if (run_test_tpl<FooFree>() == TEST_FAILED) return TEST_FAILED;
if (run_test_tpl<BarFree>() == TEST_FAILED) return TEST_FAILED; if (run_test_tpl<BarFree>() == TEST_FAILED) return TEST_FAILED;
if (run_test_holder_inheritance()== TEST_FAILED) return TEST_FAILED;
return TEST_SUCCEED; return TEST_SUCCEED;
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "serialization.hh" #include "serialization.hh"
BOOST_CLASS_EXPORT(Foo) BOOST_CLASS_EXPORT_IMPLEMENT(Foo)
template<class Archive> template<class Archive>
void Foo::serialize(Archive & ar, const unsigned int version) void Foo::serialize(Archive & ar, const unsigned int version)
...@@ -29,13 +29,14 @@ void Foo::serialize(Archive & ar, const unsigned int version) ...@@ -29,13 +29,14 @@ void Foo::serialize(Archive & ar, const unsigned int version)
HPP_SERIALIZATION_IMPLEMENT(Foo); HPP_SERIALIZATION_IMPLEMENT(Foo);
BOOST_CLASS_EXPORT(Bar) BOOST_CLASS_EXPORT_IMPLEMENT(Bar)
template<class Archive> template<class Archive>
void Bar::load(Archive & ar, const unsigned int version) void Bar::load(Archive & ar, const unsigned int version)
{ {
(void) version; (void) version;
ar & hpp::serialization::make_nvp("i", i_); ar & hpp::serialization::make_nvp("i", i_);
f_ = hpp::serialization::cast(ar).template get<Foo>("foo", true);
} }
template<class Archive> template<class Archive>
......
...@@ -26,22 +26,26 @@ class Foo { ...@@ -26,22 +26,26 @@ class Foo {
HPP_SERIALIZABLE(); HPP_SERIALIZABLE();
}; };
BOOST_CLASS_EXPORT_KEY(Foo)
class Bar { class Bar {
public: public:
Bar(int i) : i_ (i) {}; Bar(int i) : i_ (i) {};
int i_; int i_;
Foo* f_;
private: private:
Bar() {} Bar() {}
HPP_SERIALIZABLE_SPLIT(); HPP_SERIALIZABLE_SPLIT();
}; };
BOOST_CLASS_EXPORT_KEY(Bar)
class FooFree { class FooFree {
public: public:
FooFree(int i) : i_ (i) {}; FooFree(int i) : i_ (i) {};
int i_; int i_;
private:
FooFree() {} FooFree() {}
}; };
...@@ -52,7 +56,6 @@ class BarFree { ...@@ -52,7 +56,6 @@ class BarFree {
BarFree(int i) : i_ (i) {}; BarFree(int i) : i_ (i) {};
int i_; int i_;
private:
BarFree() {} BarFree() {}
}; };
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment