c++ 增强序列化:cpp中的保存/加载构造数据未与hpp链接

ou6hu8tu  于 2023-01-18  发布在  其他
关注(0)|答案(1)|浏览(109)

我不允许在头文件中放置任何(模板)函数定义。我有一个没有标准构造函数的类。
为了使序列化在没有构造函数的情况下工作,我使用了save_construct_dataload_construct_data。我在头文件中有这样的声明。但是它们似乎没有与cpp文件中的定义链接起来。我的意思是,serialize函数链接了,但是实际上并没有进行很多序列化。
声明:

// Foo.h
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>

#include <boost/archive/xml_oarchive.hpp>
#include <boost/archive/xml_iarchive.hpp>

#include <iostream>

class Foo {
    friend class boost::serialization::access;
    int m_bar;

    template<class Archive>
    void serialize(Archive & ar, const unsigned int file_version);
public:
    Foo(int bar);
    int getBar() const;
};

BOOST_CLASS_EXPORT(Foo);

namespace boost_1_69_0 {
namespace serialization {
template<class Archive>
void save_construct_data(Archive& ar, const Foo* t, const unsigned int version);

template<class Archive>
void load_construct_data(Archive& ar, Foo* t, const unsigned int version);
}
}

定义:
x一个一个一个一个x一个一个二个x
哦,顺便说一句,我不知道为什么,但是如果我调用名称空间boost而不是boost_1_69_0,我会得到错误:error: namespace alias ‘boost’ not allowed here, assuming ‘boost_1_69_0’,我不知道为什么。

dtcbnfnu

dtcbnfnu1#

有很多事情(除了我们前面讨论过的boost_1_69_0之外)。
1.首先,在Foo.cpp定义专门化:

template <> void serialize(boost::archive::xml_oarchive& ar, unsigned int const file_version) {}
template <> void serialize(boost::archive::xml_iarchive& ar, unsigned int const file_version) {}

但是这样的模板并不存在,只有成员函数模板serialize,另外,你可以示例化任何你想要的,但是除非它在头文件中是**extern-declared**,否则编译器将拒绝示例化没有定义的模板。
1.加载/保存构造数据函数也会发生同样的情况(缺少外部声明)。
1.您选择将load/save构造数据放在boost::serialization名称空间中,这很奇怪,因为显然您希望在头文件中隐藏实现细节,我会在最少干扰和最少紧密耦合的位置定义函数:在类命名空间中。

  1. BOOST_CLASS_EXPORT(Foo)也有类似的情况--因为这是在头文件中,所以您将在多个翻译单元中定义内容。
    通过分离声明和定义来避免它(或者,你知道,选择不做这种雨舞)
    1.顶级const在函数签名中(字面上)毫无意义
    1.为什么你要在头文件中包含整个归档文件呢?我得说包含这些文件比包含你试图隐藏的模板函数的定义要有100倍的影响力...
    1.与iostream相同
    1.你正在导出一个非多态类。这将无法编译。你需要一个虚拟接口。
    1.你没有通过指针进行序列化;多态类型没有意义,加载/保存构造数据也没有意义
    运行这些代码,我会得到以下"更理想"的标题:
// Foo.hpp
#include <boost/serialization/export.hpp>

// forward declarations
namespace boost { namespace archive { class xml_iarchive; } }
namespace boost { namespace archive { class xml_oarchive; } }
namespace boost { namespace serialization { class access; } }

struct IFoo {
    virtual ~IFoo() = default;
};

class Foo : public IFoo {
    int m_bar;

    friend class boost::serialization::access;
    using iarchive = boost::archive::xml_iarchive;
    using oarchive = boost::archive::xml_oarchive;

    friend void serialize(iarchive&, Foo&, unsigned);
    friend void serialize(oarchive&, Foo&, unsigned);
    friend void load_construct_data(iarchive& ar, Foo*, unsigned);
    friend void save_construct_data(oarchive& ar, Foo const*, unsigned);

  public:
    Foo(int bar);
    int getBar() const;
};

BOOST_CLASS_EXPORT_KEY(Foo)

相应的实现:

// Foo.cpp
#include "Foo.hpp"
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

Foo::Foo(int bar) : m_bar(bar){};

int Foo::getBar() const { return m_bar; }

void serialize(Foo::iarchive&, Foo&, unsigned) {}
void serialize(Foo::oarchive&, Foo&, unsigned) {}

void save_construct_data(Foo::oarchive& ar, Foo const* p, unsigned) {
    int bar = p->getBar();
    ar << BOOST_NVP(bar);
}

void load_construct_data(Foo::iarchive& ar, Foo* p, unsigned) {
    int bar;
    ar >> BOOST_NVP(bar);
    ::new (p) Foo(bar);
}

BOOST_CLASS_EXPORT_IMPLEMENT(Foo)

现在,稍微修正一下main

#include "Foo.hpp"
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <iostream>

int main() {
    auto foo = std::make_unique<Foo>(42);
    boost::archive::xml_oarchive oa(std::cout);
    oa << BOOST_NVP(foo);
}

图纸:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<foo class_id="0" tracking_level="0" version="0">
    <tx class_id="1" tracking_level="1" version="0" object_id="_0">
        <bar>42</bar>
    </tx>
</foo>
</boost_serialization>

看它**Live On Wandbox**

奖金

如果你真的隐藏了实现,为什么不完全隐藏它呢?那更容易,可以虚拟化等等。它伤害了我的感情,你要去这些长度"隐藏"的细节,但仍然包括主程序内的存档类型...并允许用户不匹配的序列化/反序列化,例如通过使用不同的XML名称,或者完全不同的变量类型(这导致未定义的行为)。
让我们制作一个理想的标题:

#include <iosfwd>
#include <memory>

// Foo.hpp
struct IFoo {
    virtual ~IFoo() = default;

    void save(std::ostream&) const;
    static std::unique_ptr<IFoo> load(std::istream&);
};

class Foo : public IFoo {
    int m_bar;

  public:
    Foo(int bar);
    int getBar() const;
};

我希望你会同意,* 这 * 是削减依赖性和减少耦合。其余的只是口头上的服务。
现在所有真正与序列化相关的东西都可以在cpp中了,在一个真实的项目中,你会有一个TU专门用来序列化整个对象图(最简单的方法就是把它放在IFoo.cpp中)。
让我们加入base_object序列化(以前没有):

// Foo.cpp
#include "Foo.hpp"

Foo::Foo(int bar) : m_bar(bar){}

int Foo::getBar() const { return m_bar; }

#include <boost/serialization/base_object.hpp>
#include <boost/serialization/nvp.hpp>
using boost::make_nvp;
using boost::serialization::base_object;

template <typename Archive> void serialize(Archive&, IFoo&, unsigned) {}

template <typename Archive> void serialize(Archive& ar, Foo& f, unsigned) {
    ar & boost::make_nvp("IFoo", base_object<IFoo>(f));
}

template <typename Archive> void save_construct_data(Archive& ar, Foo const* p, unsigned) {
    int bar = p->getBar();
    ar << BOOST_NVP(bar);
}

template <typename Archive> void load_construct_data(Archive& ar, Foo* p, unsigned) {
    int bar;
    ar >> BOOST_NVP(bar);
    ::new (p) Foo(bar);
}

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/export.hpp>
BOOST_CLASS_EXPORT(IFoo)
BOOST_CLASS_EXPORT(Foo)

/*static*/ std::unique_ptr<IFoo> IFoo::load(std::istream& is) {
    boost::archive::xml_iarchive ia(is);
    IFoo* p = nullptr;
    ia >> boost::make_nvp("p", p);
    return std::unique_ptr<IFoo>(p);
}

void IFoo::save(std::ostream& os) const {
    boost::archive::xml_oarchive oa(os);
    IFoo const* p = this;
    oa << make_nvp("p", p);
}

扩展main以显示往返反序列化:

#include "Foo.hpp"
#include <iostream>
#include <sstream>

int main() {
    Foo(42).save(std::cout);

    std::stringstream ss;
    Foo(99).save(ss);
    auto roundtrip = Foo::load(ss);

    if (auto foo = dynamic_cast<Foo*>(roundtrip.get())) {
        std::cout << "roundtrip is a foo, getBar(): " << foo->getBar() << "\n";
    }
}

图纸

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<p class_id="1" class_name="Foo" tracking_level="1" version="0" object_id="_0">
        <bar>42</bar>
        <IFoo class_id="0" tracking_level="1" version="0" object_id="_1"></IFoo>
</p>
</boost_serialization>

roundtrip is a foo, getBar(): 99

再看一遍**Live On Wandbox**。

相关问题