#c #serialization #boost #deserialization
#c #сериализация #повышение #десериализация
Вопрос:
Некоторые классы имеют одинаковые переменные, и все они нуждаются в сериализации, поэтому я хочу определить абстрактный класс, который имеет две чисто виртуальные функции serialize()
, deserialize()
чтобы эти классы могли наследовать его и переопределять функции.
Однако я обнаружил, что все переопределенные функции представляют собой повторяющийся код, например, следующий:
void Derived::serialize() {
...
ofstream ofs(file, ios::binary);
boost::archive::binary_oarchive oa(ofs);
oa << (*this);
}
void Derived::deserialize() {
...
ifstream ifs(file, ios::binary);
boost::archive::bianry_iarchive ia(ifs);
ia >> (*this);
}
Как я могу решить эту проблему?
Комментарии:
1. Является ли шаблон разработки шаблона жизнеспособным способом? sourcemaking.com/design_patterns/template_method Вы можете сгруппировать общий код вместе.
Ответ №1:
Я наблюдаю, что вы сериализуете *this
, что есть Baseamp;
. Поскольку это не тип указателя, вы не можете десериализовать производные типы. Это заставляет меня задаться вопросом, какая иерархия классов у вас действительно есть.
Я представляю полные примеры:
- Невиртуальная иерархия
- Виртуальная иерархия / полиморфная сериализация
1. Невиртуальная иерархия
Вы могли бы использовать CRTP для выполнения чего-то, что зависит от производного типа:
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/array.hpp>
#include <fstream>
template <typename Derived> class Base {
public:
void serialize() const
{
//...
std::ofstream ofs(file, std::ios::binary);
boost::archive::binary_oarchive oa(ofs);
oa << as_derived();
}
void deserialize() {
//...
std::ifstream ifs(file, std::ios::binary);
boost::archive::binary_iarchive ia(ifs);
ia >> as_derived();
}
protected:
std::string file = "base.bin";
private:
int base_data = 42;
Derivedamp; as_derived() { return static_cast<Derivedamp;>(*this); } ;
Derived constamp; as_derived() const { return static_cast<Derivedamp;>(*this); } ;
friend class boost::serialization::access;
template <typename Ar> void serialize(Aramp; ar, unsigned)
{
ar
amp; base_data
amp; as_derived().common_derived_data
;
}
};
struct Derived1 : Base<Derived1> {
private:
friend class Base;
std::array<float, 30> common_derived_data = {1,1,1,1};
};
struct Derived2 : Base<Derived2> {
private:
friend class Base;
std::array<float, 30> common_derived_data = {2,2,2,2};
};
int main() {
Derived1 x;
Derived2 y;
x.serialize();
system("xxd base.bin");
x.deserialize();
y.serialize();
system("xxd base.bin");
x.deserialize();
}
Примечание:
Это больше не виртуальная иерархия. Полиморфные типы накладывают ограничения на ускоренную сериализацию. Вы захотите придерживаться подхода Boost. Возможно, используйте простой бесплатный шаблон функции, чтобы помочь с повторяющимся кодом?
2. Виртуальная иерархия / полиморфная сериализация
Пример:
Примечание:
- сериализация ДОЛЖНА осуществляться через указатель
- сериализация ДОЛЖНА осуществляться через указатель на базовый (или, по крайней мере, того же типа, что и сериализованный всегда, поэтому базовый класс — это единственное, что имеет смысл)
- типы ДОЛЖНЫ быть зарегистрированы (или предполагаться абстрактными), см. BOOST_CLASS_EXPORT и др.
- шаблон
repeated_serialization_code
функции теперь является домом для повторяющегося кода сериализации. - обратите внимание, что это ДОЛЖНО включать
base_object
This
может быть выведено const / non-const в зависимости от контекста вызова.
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/array.hpp>
#include <boost/serialization/export.hpp>
#include <fstream>
class Base {
public:
using Ptr = std::unique_ptr<Base>;
virtual ~Base() = default;
static void serialize(std::string file, Ptr constamp; ptr)
{
//...
std::ofstream ofs(file, std::ios::binary);
boost::archive::binary_oarchive oa(ofs);
oa << ptr;
}
static Ptr deserialize(std::string file) {
Ptr ptr;
//...
std::ifstream ifs(file, std::ios::binary);
boost::archive::binary_iarchive ia(ifs);
ia >> ptr;
return ptr;
}
protected:
int base_data = 42;
friend class boost::serialization::access;
template <typename Ar> void serialize(Aramp; ar, unsigned) { aramp; base_data; }
template <typename Ar, typename This>
static inline void repeated_serialization_code(Aramp; ar, Thisamp; self, unsigned)
{
ar amp; boost::serialization::base_object<Base>(self)
amp; self.common_derived_data;
}
};
struct Derived1 : Base {
private:
friend class Mixin;
std::array<float, 30> common_derived_data = {1,1,1,1};
friend class boost::serialization::access;
friend class Base;
template <typename Ar> void serialize(Aramp; ar, unsigned v) {
repeated_serialization_code(ar, *this, v);
}
};
struct Derived2 : Base {
private:
friend class Mixin;
std::array<float, 30> common_derived_data = {2,2,2,2};
friend class boost::serialization::access;
friend class Base;
template <typename Ar> void serialize(Aramp; ar, unsigned v) {
repeated_serialization_code(ar, *this, v);
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(Base)
BOOST_CLASS_EXPORT(Derived1)
BOOST_CLASS_EXPORT(Derived2)
int main() {
Base::Ptr x = std::make_unique<Derived1>();
Base::Ptr y = std::make_unique<Derived2>();
Base::serialize("x.bin", x);
system("xxd x.bin");
auto x2 = Base::deserialize("x.bin");
Base::serialize("y.bin", y);
system("xxd y.bin");
auto y2 = Base::deserialize("y.bin");
}
Комментарии:
1. Добавлена вторая демонстрация, демонстрирующая подход к иерархии виртуальных классов. Смотрите Примечания для всех важных вещей, на которые следует обратить внимание!
2. (третьим вариантом может быть перемещение членов / кода общего производного класса в тип Mixin, чтобы вы унаследовали `Base -> Mixin -> Derived *)