Проблема с сериализацией function_wrapper

Рейтинг: 0Ответов: 1Опубликовано: 01.08.2023

Есть такой код.

#include <iostream>
#include <memory>
#include <fstream>
#include <boost/serialization/serialization.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/export.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/assume_abstract.hpp>
using namespace std;

struct impl_base {
        virtual void call() = 0;
        virtual ~impl_base() {}
    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive & ar, const unsigned int file_version){}
    };
    BOOST_SERIALIZATION_ASSUME_ABSTRACT(impl_base)

    template<typename F>
    struct impl_type : impl_base {
        F f;
        impl_type(F&& f_) : f{std::move(f_)} {}
        void call() { f(); }
    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int version) {
            ar & boost::serialization::base_object<impl_base>(*this) & f;
        }
    };

class function_wrapper {
    std::unique_ptr<impl_base> impl;
public:
    template<typename F>
    function_wrapper(F&& f) : impl(new impl_type<F>(std::move(f))) {}
    void operator()() { impl->call(); }
    function_wrapper() = default;
    function_wrapper(function_wrapper&& other) : impl(std::move(other.impl)) {}
    function_wrapper& operator=(function_wrapper&& other)
    {
        impl = std::move(other.impl);
        return *this;
    }
    function_wrapper(const function_wrapper&) = delete;
    function_wrapper(function_wrapper&) = delete;
    function_wrapper& operator=(const function_wrapper&) = delete;
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive& ar, const unsigned int version) { 
        ar & impl; 
    }
};

BOOST_CLASS_EXPORT(function_wrapper)

void hel() { cout << "WTF" << endl;}

void serialize_task()
{
    function_wrapper task(std::bind(&hel));
    task();
    std::ofstream ofs("task.dat");
    boost::archive::text_oarchive oar(ofs);
    oar << task;
    ofs.close();

    // Загрузка объекта из файла
    std::ifstream ifs("task.dat");
    boost::archive::text_iarchive iar(ifs);
    function_wrapper loaded_task;
    iar >> loaded_task;
    ifs.close();

    // Проверка результатов сериализации
    loaded_task();
}

int main() {
    serialize_task();
    return 0;
}

Есть базовый класс impl_base, для которого применяю BOOST_SERIALIZATION_ASSUME_ABSTRACT(impl_base), как показано в офиц.документации serialization. И дальше я пытаюсь тестить при помощиvoid serialize_task(). (bind нужен, он на своем месте).

Вопрос в целом - что же надо зарегистрировать, чтобы ошибка

terminate called after throwing an instance of 'boost::archive::archive_exception'
  what():  unregistered class - derived class not registered or exported`

перестала высовываться??? P.S. Boost последней версии, собираю при помощи g++ -o a a.cpp -lboost_serialization.


Как подсказал @user7860670 - у меня все-таки нет регистрации impl_type. Тогда я вынес его и impl_base из function_wrapper и добавил к коду регистрацию через экспорт BOOST_CLASS_EXPORT(impl_type<function<void()>>) для объекта типа function<void()>, так как у меня в примере должна вызваться функция void hell(){..}:

struct impl_base {
        virtual void call() = 0;
        virtual ~impl_base() {}
    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive & ar, const unsigned int file_version){}
    };
    BOOST_SERIALIZATION_ASSUME_ABSTRACT(impl_base)

    template<typename F>
    struct impl_type : impl_base {
        F f;
        impl_type(F&& f_) : f{std::move(f_)} {}
        void call() { f(); }
    private:
        friend class boost::serialization::access;
        template<class Archive>
        void serialize(Archive& ar, const unsigned int version) {
            ar & boost::serialization::base_object<impl_base>(*this) & f;
        }
    };
BOOST_CLASS_EXPORT(impl_type<function<void()>>)

class function_wrapper {....

Но это не помогло, так как начала появляться другая ошибка:

error: ‘class std::function<void()>’ has no member named ‘serialize’
  116 |         t.serialize(ar, file_version);

Что скорее всего свидетельствует о том, что объект ‘class std::function<void()>’ нельзя сериализовать.

Мой вопрос - это так или нет? Если мои мысли верны, то вопрос можно закрыть, если нет, то прошу совета, что исправить в коде.

Ответы

▲ 1Принят

@user7860670 дал ответ на мой вопрос. Ошибка была в том, что не было конструктора по умолчанию для impl_type. Плюс нет сериализации для типа function, ее нужно самостоятельно обработать ручками - сохранить "имя вызываемой функции" и для нее аргументы. Потом при десериализации "узнав имя функции" воссоздать ее снова, закинув в нее аргументы.