Ошибка сериализации абстрактного класса с повышением

Я пытаюсь сериализовать свои структуры данных, чтобы записать их в сокет tcp.

До сих пор я обнаружил, что моя проблема заключается в сериализации. Я даже пытался использовать

BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)

но я не могу найти ни одного рабочего примера, похожего на мою программу, и как ее правильно реализовать.

Вот некоторые из ссылок, которые я посетил:

Мои структуры данных немного сложнее, чем эта, но давайте предположим, что у меня есть следующая структура

Координата.ч

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>

class Coordinate {
public:
    Coordinate() {}
    Coordinate(int c) : c(c) {}
    int get(void) { return c; }
    std::string toString(void);
private:
    int c;
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive &ar, const unsigned int version) {
        ar & this->c;
    }
};

Переместить.ч

class Coordinate;

#include "Coordinate.h"

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>

class Move {
public:
    Move() {}
    ~Move() {}
    Coordinate* getCoordinate(void) {return this->destination; }
    virtual bool isJump(void) = 0;
protected:
    Coordinate *destination;
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive &ar, const unsigned int version) {
        ar & this->destination;
    }
};

MoveNormal.h

class Coordinate;

#include "Move.h"
#include "Coordinate.h"

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>

class MoveNormal : public Move {
public:
    MoveNormal() {}
    MoveNormal(Coordinate *destination) { this->destination = destination; }
    ~MoveNormal() {}
    virtual bool isJump(void);
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serialize(Archive &ar, const unsigned int version) {
        ar & boost::serialization::base_object<Move>(*this);
    }
};

Здесь определяются виртуальные методы.

MoveNormal.cpp

#include "MoveNormal.h"

bool MoveNormal::isJump(void) {
    return false;
}

Мой main.cpp выглядит так:

#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"

#include <fstream>

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>

int main(int argc, char *argv[]) {
    Coordinate *c = new Coordinate(10);
    // This runs OK
    /*
    {
        std::ofstream ofs("f.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << c;
    }
    Coordinate *d;
    {
        std::ifstream ifs("f.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> d;
    }
    std::cout << "c.get(): " << c->get() << std::endl;
    std::cout << "d.get(): " << d->get() << std::endl;
    */

    // This is where I get my error
    Move *m  = new MoveNormal(c);
    {
        std::ofstream ofs("f.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << m;    // Line where the error occurs
    }
    return 0;
}

Но когда я запускаю программу, я получаю следующую ошибку:

Необработанное исключение по адресу 0x76dbb9bc в Test.exe: исключение Microsoft C++: boost::archive::archive_exception в ячейке памяти 0x001df078..

Я использую VS2010 и Boost 1.48.0.


person ealves    schedule 03.12.2011    source источник
comment
объявите перегруженный оператор ‹‹ как друга класса, а затем реализуйте его. Учитывая, что ваша структура данных состоит только из значения int, это должно быть совершенно тривиально. Если вам нужно передать это через сокет, просто переинтерпретируйте_приведите int к char* и отправьте его через сокет;)   -  person johnathan    schedule 04.12.2011
comment
Дело в том, что моя структура данных не имеет только int. В приведенном выше примере это простая структура, созданная именно для этого случая. Учтите, что внутри моей структуры у меня есть указатели на другие классы и контейнеры stl. Пока я думаю, что понял, как заставить его работать. Я просто тестирую его в своем проекте, и я опубликую решение здесь, как только смогу.   -  person ealves    schedule 04.12.2011


Ответы (2)


Это немного странно, но я собираюсь ответить на свой вопрос. Я только что понял, как заставить работать мой пример выше.

Вот и решение. Каждый раз, когда нам нужно сериализовать класс, который наследует атрибуты от другого класса, нам нужно использовать макрос:

BOOST_CLASS_EXPORT(T)

Согласно документу по ускоренной сериализации

BOOST_CLASS_EXPORT в том же исходном модуле, который включает любой из заголовков классов архива, создаст экземпляр кода, необходимого для сериализации полиморфных указателей указанного типа на все эти классы архива. Если заголовки классов архива не включены, код не будет создан.

Обратите внимание, что для реализации этой функции требуется, чтобы макрос BOOST_CLASS_EXPORT появлялся после включения всех заголовков архивных классов, для которых должен быть создан код.

Итак, в моем случае мой файл main.cpp теперь:

#include <fstream>

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>

#include "Coordinate.h"
#include "Move.h"
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)

int main(int argc, char *argv[]) {
    Coordinate *c = new Coordinate(150);
    Move *m = new MoveNormal(c);
    std::cout << "m.getDestination().get(): " << m->getDestination()->get() << std::endl;
    {
        std::ofstream ofs("f.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << m;
    }

    Move *n;
    {
        std::ifstream ifs("f.txt");
        boost::archive::text_iarchive ia(ifs);
        ia >> n;
    }
    std::cout << "n.getDestination().get(): " << n->getDestination()->get() << std::endl;
    return 0;
}

Просто убедитесь, что вы включили все нужные вам архивы повышения, прежде чем использовать МАКРОС экспорта.

Чтобы закончить мой проект, помимо сериализации, мне нужно записать их в сокет tcp, используя boost:: asio.

Итак, давайте предположим, что у меня есть заголовок соединения, например этот и тот теперь у меня есть еще один класс под названием MoveJump, определенный в моем MoveJump.h

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>

#include "Coordinate.h"
#include "Move.h"

class MoveJump : public Move {
public:
    MoveJump() {}
    MoveJump(Coordinate *c) { this->destinatio = c; }
    ~MoveJump() {}
    virtual bool isJump(void);
private:
    friend class boost::serialization::access;
    template<typename Archive>
    void serializize(Archive &ar, const unsigned int version) {
        ar & boost::serialization::base_object<Move>(*this);
    }
};

Теперь, чтобы сериализовать эти структуры, мой основной вид выглядит так

#include <boost\archive\text_iarchive.hpp>
#include <boost\archive\text_oarchive.hpp>
#include <boost\serialization\export.hpp>

#include <fstream>

#include "Coordinate.h"
#include "Move.h"
// And now we register all the possible Moves
#include "MoveNormal.h"
BOOST_CLASS_EXPORT(MoveNormal)
#include "MoveJump.h"
BOOST_CLASS_EXPORT(MoveJump)

int main(int argc, char *argv[]) {
    Coordinate *c = new Coordinate(10);
    Move *m = new MoveNormal(c);
    Coordinate *d = new Coordinate(15);
    Move *j = new MoveJump(d);
    {
        std::ofstream ofs("m.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << m;
    }
    {
        std::ofstream ofs("j.txt");
        boost::archive::text_oarchive oa(ofs);
        oa << j;
    }
}

Хитрость заключается в том, чтобы зарегистрировать классы, которые будут сериализованы, когда у нас будет указатель на базовый класс.

Если внутри моего Move.h у меня больше указателей на другие базовые классы, что я и делаю в своем проекте, нам нужно включить в main все заголовки и прописать все возможные классы, расширяющие базовый класс.

Я надеюсь, что это поможет кому-то, у кого могут быть подобные проблемы в будущем.

Не стесняйтесь представить новые возможные решения.

Спасибо

person ealves    schedule 04.12.2011

Вообще говоря, вы можете просто использовать BOOST_CLASS_EXPORT для регистрации всех классов, или вы можете использовать BOOST_SERIALIZATION_ASSUME_ABSTRACT для суперкласса и использовать функцию-член register_type для «архива» вместе. см.: Как сериализовать производные классы шаблонов с помощью Boost.serialize? для подробностей (извините за мой плохой английский:))

person xiaosuiba    schedule 26.11.2013