使用boost :: serialization将派生类指针序列化为向量时出现问题

时间:2019-07-11 08:17:10

标签: c++ serialization boost boost-serialization

我有一段代码,其中定义了以下类:BaseDerivedContaienr

Derived显然继承了Base,而Container包含Base个共享指针的向量,该指针可以是Base和{{1}的指针}。

我想序列化Derived,以便将向量的元素分别序列化为ContainerBase,但似乎不起作用。

这是我的测试代码:

Derived

运行程序时,我打印#include <boost/archive/xml_oarchive.hpp> #include <boost/archive/xml_iarchive.hpp> #include <boost/serialization/base_object.hpp> #include <boost/serialization/vector.hpp> #include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/string.hpp> #include <boost/serialization/export.hpp> #include <iostream> #include <string> #include <vector> #include <memory> #include <sstream> /// Base class /// class Base { public: using Ptr = std::shared_ptr<Base>; public: void setDouble(double d) { m_d = d; } void setInteger(int c) { m_c = c; } double getDouble() const { return m_d; } int getInteger() const { return m_c; } private: double m_d; int m_c; }; /// Derived class from Base /// class Derived : public Base { public: using Ptr = std::shared_ptr<Derived>; public: void setString(const std::string& s) { m_s = s; } const std::string& getString() const { return m_s; } private: std::string m_s; }; /// Container of base class pointers /// class Container { public: void addData(Base::Ptr data) { m_data.push_back(data); } const std::vector<Base::Ptr>& getDataVector() const { return m_data; } private: std::vector<Base::Ptr> m_data; }; BOOST_SERIALIZATION_SPLIT_FREE(Base) BOOST_SERIALIZATION_SPLIT_FREE(Derived) BOOST_SERIALIZATION_SPLIT_FREE(Container) BOOST_CLASS_EXPORT_GUID(Derived, "Derived") namespace boost { namespace serialization { /// Serialization of base class /// template<class Archive> void save(Archive& ar, const Base& m, unsigned int) { auto d = m.getDouble(); auto i = m.getInteger(); ar& make_nvp("doublevalue", d); ar& make_nvp("intvalue", i); } template<class Archive> void load(Archive& ar, Base& m, unsigned int) { double d; int i; ar& make_nvp("doublevalue", d); ar& make_nvp("intvalue", i); m.setDouble(d); m.setInteger(i); } /// serialization of derived class /// template<class Archive> void save(Archive& ar, const Derived& m, unsigned int) { ar& make_nvp("base", base_object<const Base>(m)); ar& make_nvp("stringvalue", m.getString()); } template<class Archive> void load(Archive& ar, Derived& m, unsigned int) { std::string s; ar& make_nvp("base", base_object<Base>(m)); ar& make_nvp("stringvalue", s); m.setString(s); } /// serialization of container class /// template<class Archive> void save(Archive& ar, const Container& m, unsigned int) { ar& make_nvp("data", m.getDataVector()); } template<class Archive> void load(Archive& ar, Container& m, unsigned int) { std::vector<Base::Ptr> data; ar& make_nvp("data", data); for (const auto& it : data) { m.addData(it); } } } } // namespace boost::serialization int main(int argc, char *argv[]) { // Initialize container Container container; auto baseObj = std::make_shared<Base>(); baseObj->setDouble(4.3); baseObj->setInteger(6); auto derivedObj = std::make_shared<Derived>(); derivedObj->setDouble(1.1); derivedObj->setInteger(2); derivedObj->setString("string in derived"); container.addData(baseObj); container.addData(derivedObj); // Print serialization of Base std::stringstream basess; boost::archive::xml_oarchive baseoa{basess}; baseoa << boost::serialization::make_nvp("baseclass", baseObj); std::cout << basess.str() << std::endl; // Print serialization of Derived std::stringstream derivedss; boost::archive::xml_oarchive derivedoa{derivedss}; derivedoa << boost::serialization::make_nvp("derivedclass", derivedObj); std::cout << derivedss.str() << std::endl; // Print serialization of Container std::stringstream containerss; boost::archive::xml_oarchive containeroa{containerss}; containeroa << boost::serialization::make_nvp("containerclass", container); std::cout << containerss.str() << std::endl; return 0; } 的序列化,它是baseObj的共享指针:

Base

这似乎是正确的,因为我在基类中同时定义了<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <!DOCTYPE boost_serialization> <boost_serialization signature="serialization::archive" version="17"> <baseclass class_id="0" tracking_level="0" version="1"> <px class_id="1" tracking_level="1" version="0" object_id="_0"> <doublevalue>4.29999999999999982e+00</doublevalue> <intvalue>6</intvalue> </px> </baseclass> doublevalue

然后我打印intvalue的序列化,它是derivedObj的共享指针:

Derived

由于我拥有基类数据以及派生类的<!DOCTYPE boost_serialization> <boost_serialization signature="serialization::archive" version="17"> <derivedclass class_id="0" tracking_level="0" version="1"> <px class_id="1" tracking_level="1" version="0" object_id="_0"> <base class_id="2" tracking_level="1" version="0" object_id="_1"> <doublevalue>1.10000000000000009e+00</doublevalue> <intvalue>2</intvalue> </base> <stringvalue>string in derived</stringvalue> </px> </derivedclass> ,因此似乎可以按预期工作。

现在,如果我将两个指针都放在stringvalue的{​​{1}}中,则期望正确地序列化std::vector<std::shared_ptr<Base>>Container。相反,这是输出:

baseObj

向量的两个元素都被序列化为derivedObj指针。

我已尝试按照文档中的建议使用<?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <!DOCTYPE boost_serialization> <boost_serialization signature="serialization::archive" version="17"> <containerclass class_id="0" tracking_level="0" version="0"> <data class_id="1" tracking_level="0" version="0"> <count>2</count> <item_version>1</item_version> <item class_id="2" tracking_level="0" version="1"> <px class_id="3" tracking_level="1" version="0" object_id="_0"> <doublevalue>4.29999999999999982e+00</doublevalue> <intvalue>6</intvalue> </px> </item> <item> <px class_id_reference="3" object_id="_1"> <doublevalue>1.10000000000000009e+00</doublevalue> <intvalue>2</intvalue> </px> </item> </data> </containerclass> 宏,但似乎不起作用。

我还尝试了this post中提出的解决方案,方法是注释Base并在BOOST_CLASS_EXPORT_GUID(Derived, "Derived")的序列化中使用BOOST_CLASS_EXPORT_GUID(Derived, "Derived"),但问题仍然存在:

register_type

如何正确序列化存储在Container共享指针向量中的/// serialization of container class /// template<class Archive> void save(Archive& ar, const Container& m, unsigned int) { ar.template register_type<Derived>(); ar& make_nvp("data", m.getDataVector()); } template<class Archive> void load(Archive& ar, Container& m, unsigned int) { ar.template register_type<Derived>() ; std::vector<Base::Ptr> data; ar& make_nvp("data", data); for (const auto& it : data) { m.addData(it); } } 类?

1 个答案:

答案 0 :(得分:1)

在派生类的情况下,问题的一部分可能是std::shared_ptr的行为。因此,有必要仅用普通指针替换std::shared_ptr

struct A
{

};

struct B : public A
{

};

void fun(const std::shared_ptr<A>& base)
{
    std::cout << typeid(base).name() << std::endl;
}

int main(int argc, char *argv[]) {
    auto a=std::make_shared<A>();
    auto b=std::make_shared<B>();

  std::cout << typeid(a).name() << std::endl;
  std::cout << typeid(b).name() << std::endl;
  fun(a);
  fun(b);
}

这会给您带来希望第二行和第四行相等的地方:

class std::shared_ptr<struct A>
class std::shared_ptr<struct B>
class std::shared_ptr<struct A>
class std::shared_ptr<struct A>

第二点但不是很明显的一点是,您的基类应至少包含一个virtual function。您只需包含以下内容即可使析构函数成为虚拟函数:

virtual ~Base() {};

文档说:

  

事实证明,序列化的对象的类型取决于基类(在这种情况下为基类)是否是多语言的。如果base不是多态的,也就是说它没有虚函数,那么base类型的对象将被序列化。任何派生类中的信息都将丢失。如果这是期望的(通常是不需要的),则无需其他努力。

在我用简单的指针替换了所有shared_ptr并添加了虚拟析构函数之后,结果就如所期望的,最后一部分得到以下输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="17">
<containerclass class_id="0" tracking_level="0" version="0">
        <data class_id="1" tracking_level="0" version="0">
                <count>2</count>
                <item_version>0</item_version>
                <item class_id="2" tracking_level="1" version="0" object_id="_0">
                        <doublevalue>4.29999999999999982e+00</doublevalue>
                        <intvalue>6</intvalue>
                </item>
                <item class_id="3" class_name="Derived" tracking_level="1" version="0" object_id="_1">
                        <base object_id="_2">
                                <doublevalue>1.10000000000000009e+00</doublevalue>
                                <intvalue>2</intvalue>
                        </base>
                        <stringvalue>string in derived</stringvalue>
                </item>
        </data>
</containerclass>