Ошибка сериализации абстрактного класса с boost - PullRequest
5 голосов
/ 04 декабря 2011

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

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

BOOST_SERIALIZATION_ASSUME_ABSTRACT(T)

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

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

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

Coordinate.h

#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;
    }
};

Move.h

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;
}

My 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.

Ответы [ 2 ]

6 голосов
/ 04 декабря 2011

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

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

BOOST_CLASS_EXPORT(T)

В соответствии с документом boost serialization

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;
}

Просто убедитесь, что вы включили все необходимые буст-архивы, прежде чем использовать экспорт MACRO.

Чтобы завершить мой проект, кроме сериализации, мне нужно записать их в сокет 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 все заголовки и зарегистрировать все возможные классы, которые расширяют базовый класс.

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

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

Спасибо

0 голосов
/ 26 ноября 2013

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

...