ускорение сериализации и десериализации для разных производных классов

#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. Невиртуальная иерархия
  2. Виртуальная иерархия / полиморфная сериализация

1. Невиртуальная иерархия

Вы могли бы использовать CRTP для выполнения чего-то, что зависит от производного типа:

Жить на Coliru

 #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. Виртуальная иерархия / полиморфная сериализация

Пример:

Жить на Coliru

Примечание:

  • сериализация ДОЛЖНА осуществляться через указатель
  • сериализация ДОЛЖНА осуществляться через указатель на базовый (или, по крайней мере, того же типа, что и сериализованный всегда, поэтому базовый класс — это единственное, что имеет смысл)
  • типы ДОЛЖНЫ быть зарегистрированы (или предполагаться абстрактными), см. 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 *)