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
......@@ -29,7 +29,7 @@ COMPUTE_PROJECT_ARGS(PROJECT_ARGS LANGUAGES CXX C)
PROJECT(${PROJECT_NAME} ${PROJECT_ARGS})
# Search for Boost.
ADD_PROJECT_DEPENDENCY(Boost REQUIRED COMPONENTS filesystem serialization)
ADD_PROJECT_DEPENDENCY(Boost REQUIRED COMPONENTS filesystem serialization)
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/find-external/TinyXML)
FIND_PACKAGE(TinyXML REQUIRED)
......@@ -65,7 +65,7 @@ SET(${PROJECT_NAME}_SOURCES
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} 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.
INCLUDE(CheckIncludeFiles)
......
......@@ -30,6 +30,10 @@
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.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) \
template void type load<archive##_iarchive>(archive##_iarchive& ar, \
......@@ -47,7 +51,6 @@
arg BOOST_PP_COMMA_IF(BOOST_PP_NOT(BOOST_PP_IS_EMPTY(arg))) \
const unsigned int ver)
#if 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,)
......@@ -72,37 +75,143 @@
_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 serialization {
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 hpp
......
......@@ -22,8 +22,8 @@
# include <iostream>
# include "config.h"
static const int TEST_FAILED = 10;
static const int TEST_SUCCEED = 0;
static constexpr int TEST_FAILED = 10;
static constexpr int TEST_SUCCEED = 0;
# define GENERATE_TEST() \
int \
......
......@@ -20,34 +20,113 @@
#include "common.hh"
#include "serialization.hh"
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <hpp/util/serialization.hh>
template<typename T>
int run_test_tpl ()
{
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);
}
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);
}
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;
}
int run_test ()
{
if (run_test_tpl<Foo >() == TEST_FAILED) return TEST_FAILED;
if (run_test_tpl<Bar >() == 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<Foo >() == 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<BarFree>() == TEST_FAILED) return TEST_FAILED;
if (run_test_holder_inheritance()== TEST_FAILED) return TEST_FAILED;
return TEST_SUCCEED;
}
......
......@@ -18,7 +18,7 @@
#include "serialization.hh"
BOOST_CLASS_EXPORT(Foo)
BOOST_CLASS_EXPORT_IMPLEMENT(Foo)
template<class Archive>
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);
BOOST_CLASS_EXPORT(Bar)
BOOST_CLASS_EXPORT_IMPLEMENT(Bar)
template<class Archive>
void Bar::load(Archive & ar, const unsigned int version)
{
(void) version;
ar & hpp::serialization::make_nvp("i", i_);
f_ = hpp::serialization::cast(ar).template get<Foo>("foo", true);
}
template<class Archive>
......
......@@ -26,22 +26,26 @@ class Foo {
HPP_SERIALIZABLE();
};
BOOST_CLASS_EXPORT_KEY(Foo)
class Bar {
public:
Bar(int i) : i_ (i) {};
int i_;
Foo* f_;
private:
Bar() {}
HPP_SERIALIZABLE_SPLIT();
};
BOOST_CLASS_EXPORT_KEY(Bar)
class FooFree {
public:
FooFree(int i) : i_ (i) {};
int i_;
private:
FooFree() {}
};
......@@ -52,7 +56,6 @@ class BarFree {
BarFree(int i) : i_ (i) {};
int i_;
private:
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