我有一段代码,其中定义了以下类:Base
、Derived
、Contaienr
。
Derived
显然继承了Base
,而Container
包含Base
共享指针的向量,可以是Base
和Derived
的指针。
我想序列化Container
以便将向量的元素序列化为Base
并尊重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
。
现在,如果我把两个指针都放在Container
的std::vector<std::shared_ptr<Base>>
中,我希望正确地序列化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>
矢量的两个元素都序列化为Base
指针。
我尝试按照文档中的建议使用BOOST_CLASS_EXPORT_GUID(Derived, "Derived")
宏,但它似乎不起作用。
我还尝试了这篇文章中提出的解决方案,通过评论BOOST_CLASS_EXPORT_GUID(Derived, "Derived")
并在Container
的序列化中使用register_type
,但问题仍然存在:
/// 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);
}
}
如何正确序列化存储在Base
共享指针向量中的Derived
类?
问题的一部分可能是派生类情况下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 ~Base() {};
文档说:
事实证明,序列化的对象类型取决于基类(在本例中为 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>