Boost.serialization未注册的类异常,在运行时链接的共享库中定义了序列化类

时间:2015-04-25 22:08:00

标签: c++ serialization boost dynamic-linking boost-serialization

我正在尝试创建一个模块化游戏系统,我希望用户定义的类能够被序列化。为此,我将放置来自多态基类的类。我试图在这个类上实现序列化时遇到了麻烦。我一直得到未注册的类异常(运行时错误)。

这是一个最小的测试用例:

环境:Windows 8.1 MSVC ++ 12(visual studio 2013)

parent_class.h - 定义多态的parent_class类

#pragma once

#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>

class parent_class
{

protected:

    friend boost::serialization::access;

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & BOOST_SERIALIZATION_NVP(x) & BOOST_SERIALIZATION_NVP(y);
    }

    float x;
    float y;

public:

    explicit parent_class(float x, float y) : x(x), y(y) {}

    // virtual deconstructor to make it polymorphic
    virtual ~parent_class()
    {
    }
};

BOOST_CLASS_EXPORT(parent_class);

Main.cpp - .exe

中唯一的.cpp
#include "parent_class.h"

#include <boost/archive/xml_oarchive.hpp>

#include <fstream>
#include <iostream>

#include <Windows.h>

typedef parent_class* addChildFun(float, float, float);

int main()
{
    // acquire module
    HMODULE module = LoadLibraryA("SerializationDLL.dll");
    assert(module);

    // acquire function ptr
    FARPROC addChildRaw = GetProcAddress(module, "makeChild");
    assert(addChildRaw);
    addChildFun* addChildPtr = reinterpret_cast<addChildFun*>(addChildRaw);

    // make polymorphic pointer
    parent_class* child = addChildPtr(325.f, 214.f, 2.5f);

    // INIT BOOST SERIALIZIZATION ARCHIVE
    std::ofstream stream{ "file.txt" };
    boost::archive::xml_oarchive arch{ stream };

    try
    {

        arch << BOOST_SERIALIZATION_NVP(child);

    }
    catch (std::exception& e)
    {
        std::cout << e.what(); // prints "unregistered class - derived class not registered or exported
    }


    std::cin.get();
    delete child;
}

最后这是我的child_class.cpp-- .dll中唯一的.cpp

#include <parent_class.h>
#include <boost/archive/xml_oarchive.hpp>


class child_class : public parent_class
{
    friend boost::serialization::access;

public:

    float z;

    explicit child_class(float x, float y, float z)
        : parent_class(x, y),
        z(z)
    {
    }

    template <typename Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & boost::serialization::make_nvp("owner", boost::serialization::base_object<parent_class>(*this));

        ar & BOOST_SERIALIZATION_NVP(z);
    }

    virtual ~child_class() override
    {

    }

};

// export the class
BOOST_CLASS_EXPORT(child_class)

// yes I am using MSVC -- hence dllexport
extern "C" __declspec(dllexport) parent_class* makeChild(float x, float y, float z)
{
    return new child_class(x, y, z);
}

所有代码都应该是不言自明的 - 如果您有任何问题,请随时发表评论。

很抱歉这是很多代码 - 我真的无法减少。

2 个答案:

答案 0 :(得分:1)

好的,经过艰苦的努力,我找到了解决方案,它存在于两行代码中。

问题似乎是存储了类的GUID和实现之间的关联的映射被定义了两次,因为它驻留在.dll和.exe都链接到的.lib文件中。显而易见的解决方案是使用Boost.serialization库的动态链接支持。

如何使用它是将#define BOOST_SERIALIZATION_DYN_LINK 1放在使用序列化库的每个.cpp文件的开头。这样,代码只被实例化一次,而boost可以在地图中找到该类。

答案 1 :(得分:0)

&#34;问题似乎是存储了类的GUID和实现之间的关联的映射被定义了两次,因为它驻留在.lib文件中,.dll和.exe都是链接到。显而易见的解决方案是使用Boost.serialization库的动态链接支持。&#34;

这个问题确实是静态链接工作方式的问题。你的强制DLL版本的解决方案将起作用,但它并不是最好的。 通过不使用内联序列化功能可以更好地解决该问题。所以替换

template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
    ar & boost::serialization::make_nvp("owner", boost::serialization::base_object<parent_class>(*this));
    ar & BOOST_SERIALIZATION_NVP(z);
}

template <typename Archive>
void serialize(Archive& ar, const unsigned int version);

并在您的孩子中包含* .cpp文件

template <typename Archive>
void serialize(Archive& ar, const unsigned int version)
{
    ar & boost::serialization::make_nvp("owner", boost::serialization::base_object<parent_class>(*this));

    ar & BOOST_SERIALIZATION_NVP(z);
}
template child_class::serialize<boost::archive::text_oarchive>;
template child_class::serialize<boost::archive::text_iarchive>;

换句话说 - 避免使用内联函数。这将保证您只获得序列化函数的一个且仅一个实例。这适用于静态和共享库构建。它还可以消除有时因违反&#34;一个定义规则而导致的混淆

BTW不要在源文件中添加e#define BOOST_SERIALIZATION_DYN_LINK 1之类的东西,而是通过make或project文件在编译器命令行中添加。这样,您可以保留将代码用作静态库,共享库或直接导入项目的功能。