将派生 class 指针序列化为具有 boost::serialization 的向量时出现问题
Problem in serialization of derived class pointer into a vector with boost::serialization
我有一段代码,其中定义了以下 classes:Base
、Derived
、Contaienr
.
Derived
显然继承了Base
,而Container
包含一个Base
共享指针的向量,可以是Base
和[=的指针18=].
我想序列化 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>
这似乎是正确的,因为我在基础 class.
中同时定义了 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>
它似乎按预期工作,因为我有基础 class 数据,还有派生 class.
的 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>
vector 的两个元素都序列化为 Base
指针。
我尝试按照文档中的建议使用 BOOST_CLASS_EXPORT_GUID(Derived, "Derived")
宏,但它似乎不起作用。
我也尝试了 this post 中提出的解决方案,通过评论 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
class?
问题的一部分可能是 std::shared_ptr
在派生 classes 的情况下的行为。因此,您有必要将 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>
第二个但不是很明显的一点是,你的基础 class 应该至少包含一个 virtual function。您可以通过包含以下内容使析构函数成为虚函数:
virtual ~Base() {};
文档说:
It turns out that the kind of object serialized depends upon whether the base class (base in this case) is polymophic or not. If base is not polymorphic, that is if it has no virtual functions, then an object of the type base will be serialized. Information in any derived classes will be lost. If this is what is desired (it usually isn't) then no other effort is required.
在我用普通指针替换所有 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>
我有一段代码,其中定义了以下 classes:Base
、Derived
、Contaienr
.
Derived
显然继承了Base
,而Container
包含一个Base
共享指针的向量,可以是Base
和[=的指针18=].
我想序列化 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>
这似乎是正确的,因为我在基础 class.
中同时定义了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>
它似乎按预期工作,因为我有基础 class 数据,还有派生 class.
的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>
vector 的两个元素都序列化为 Base
指针。
我尝试按照文档中的建议使用 BOOST_CLASS_EXPORT_GUID(Derived, "Derived")
宏,但它似乎不起作用。
我也尝试了 this post 中提出的解决方案,通过评论 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
class?
问题的一部分可能是 std::shared_ptr
在派生 classes 的情况下的行为。因此,您有必要将 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>
第二个但不是很明显的一点是,你的基础 class 应该至少包含一个 virtual function。您可以通过包含以下内容使析构函数成为虚函数:
virtual ~Base() {};
文档说:
It turns out that the kind of object serialized depends upon whether the base class (base in this case) is polymophic or not. If base is not polymorphic, that is if it has no virtual functions, then an object of the type base will be serialized. Information in any derived classes will be lost. If this is what is desired (it usually isn't) then no other effort is required.
在我用普通指针替换所有 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>