加速序列化,按基本类型加载存档的类会产生错误的数据 [英] Boost serialization, loading an archived class by base type gives wrong data
问题描述
在我将其实现到项目中之前,我编写了一个示例程序来弄清楚Boost的序列化库,但是,我有一些无法解释的行为.
I wrote an example program to figure out Boost's serialization library before I implemented it into a project, however, I got some unexplained behavior.
在我的示例中,我有两个类:通用的 BaseClass
和专门的 DerivedClass
(类似于我打算使用Boost的方式). BaseClass
只有一个成员,一个名为 name
的字符串,默认为"BaseClass". DerivedClass
公开继承 BaseClass
,将 name
设置为其他名称,并具有自己的唯一成员 data
.
In my example, I had two classes: a generic BaseClass
and a specialized DerivedClass
(analogous to what I plan to use Boost for). BaseClass
only has one member, a string called name
, which defaults to "BaseClass". DerivedClass
publicly inherits BaseClass
, sets name
to something else and has its own unique member, data
.
在主程序中,我创建一个 DerivedClass
并将 data
设置为特殊的东西",并创建一个 BaseClass
并使用名称
常规内容".我将这两个都写入 boost :: archive :: text_oarchive
的文件中,然后读回第一个对象 DerivedClass
,两次(重新创建 std::ifstream
两次).第一次读回来,我把它放在了 BaseClass *
上.调用 BaseClass :: printData()
(一种打印 std :: typeid
和 name
的虚拟方法)将按照以下内容打印:/p>
In the main program, I create a DerivedClass
with data
set to "special cool stuff", and a BaseClass
with name
"regular stuff". I write both of these to a file with boost::archive::text_oarchive
, and read the first object, the DerivedClass
, back twice (recreating the std::ifstream
both times). The first time reading it back, I put it to a BaseClass*
. Calling BaseClass::printData()
(a virtual method that prints the std::typeid
and name
) prints something along the lines of:
--- Storage done, now loading the first object as BaseClass ---
9BaseClass: 0
接下来,当我将其加载为 DerivedClass *
并调用 DerivedClass :: printData()
(从 BaseClass
覆盖以包括成员 data
在输出中)正确打印:
Next, when I load it as a DerivedClass*
and call DerivedClass::printData()
(which is overridden from BaseClass
to include the member data
in the output) correctly prints:
--- Storage done, now loading the first object as DerivedClass ---
12DerivedClass: DerivedClass AND special cool stuff
在我正在写入的文件中,我看到了:
Looking in the file I'm writing to, I see this:
22 serialization::archive 15 0 1 0
0 1 0
1 12 DerivedClass 18 special cool stuff 1
2 13 regular stuff
当我在原始的,预序列化的 DerivedClass
上调用 BaseClass :: printData()
时,我得到了:
And when I call BaseClass::printData()
on the original, pre-serializing DerivedClass
, I get this:
9BaseClass: DerivedClass
很显然,正确存储了 DerivedClass
.关于将其作为 BaseClass
加载以检查 name
的某件事搞砸了.我想不出为什么它会给我一个包含 0
的 std :: string
.
Obviously, the DerivedClass
is being stored correctly. Something about loading it as a BaseClass
to check name
is messing up. I cannot think of why it would give me a std::string
containing 0
.
我才刚刚开始学习如何使用该库,并且我在网上找到的大多数类似问题和文档都无效(即,使用 BOOST_EXPORT_CLASS
或 BOOST_CLASS_TYPE_INFO
,尽管很可能是我没有正确使用它们.)
I'm just starting to learn how to use this library, and most of the similar questions and documentation I've found online have no effect (ie, using BOOST_EXPORT_CLASS
or BOOST_CLASS_TYPE_INFO
, although it could very well be I was using them incorrectly).
这是我的代码:
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/nvp.hpp>
#include "baseclass.h"
#include "derivedclass.h"
int main() {
BaseClass* testBase = new BaseClass("regular stuff");
DerivedClass* testDerivate = new DerivedClass("special cool stuff");
testDerivate->BaseClass::printData();
std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;
std::ofstream output("storage");
{
boost::archive::text_oarchive boost_out(output);
boost_out << (testDerivate);
testDerivate->printData();
boost_out << (testBase);
testBase->printData();
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;
{
std::ifstream input("storage");
BaseClass* base;
boost::archive::text_iarchive boost_in(input);
boost_in >> (base);
base->printData();
input.close();
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as DerivedClass" << " --- " << std::endl;
{
std::ifstream input("storage");
DerivedClass* derive;
boost::archive::text_iarchive boost_in(input);
boost_in >> (derive);
derive->printData();
input.close();
}
return 0;
}
baseclass.h
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
class BaseClass
{
public:
BaseClass() {
name = "BaseClass";
}
BaseClass(std::string custom) {
name = custom;
}
virtual ~BaseClass() {}
virtual void printData() {
std::cout << typeid(*this).name() << ": " << name << std::endl;
}
protected:
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & (name);
}
};
derivedclass.hpp
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>
#include "baseclass.h"
class DerivedClass : public BaseClass
{
public:
DerivedClass() : BaseClass("DerivedClass") {}
DerivedClass(std::string custom) : BaseClass("DerivedClass") {
data = custom;
}
virtual ~DerivedClass() {}
void printData() override {
std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
}
protected:
std::string data;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version) {
ar & (boost::serialization::base_object<BaseClass>(*this));
ar & (data);
}
};
很抱歉,如果这有点长,我想尽可能地描述一下.我对使用Boost还是很陌生,对C ++的使用还不是很了解,所以即使您对我的代码有一些一般性的评论,也还是很感激的.
Sorry if this is a bit long, I wanted to be as descriptive as possible. I'm very new to using Boost and I'm not all that experienced with C++, so even if you just have some general comments on my code, I'd appreciate it.
推荐答案
您没有加载与序列化相同的 types .
You're not loading the same types as you're serializing.
因此,虽然可以说:
Base* b = new Derived();
boost_out << b;
并反序列化:
Base* b = nullptr;
boost_in >> b;
您无法序列化 Derived *
,并将其反序列化为 Base *
,反之亦然.
You cannot serialize a Derived*
and deserialize it as Base*
or vice versa.
因此,如果您知道接收代码必须支持所有派生类,请使其明确并序列化 Base *
.
So, if you know the receiving code must support all derived classes, make it explicit and serialize a Base*
.
要让反序列化端知道在反序列化多态基址指针时可能遇到的一组派生类型集,请导出这些类型.
To let the de-serializing end know what the possible set of derived types is that could be encountered in deserializing a polymorphic base pointer, export the types.
- https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/traits.html#export
-
另请参见 https://www.boost.org/doc/libs/1_67_0/libs/serialization/doc/special.html#export :
在"a.hpp"标头中包含
BOOST_CLASS_EXPORT
本身与其他序列化特征一样,将导致很难或不可能遵循上述有关在BOOST_CLASS_EXPORT之前包含存档标头的规则
被调用.最好使用标题声明中的BOOST_CLASS_EXPORT_KEY
和类定义文件中的BOOST_CLASS_EXPORT_IMPLEMENT
解决此问题.
Including
BOOST_CLASS_EXPORT
in the "a.hpp" header itself as one would do with other serialization traits will make it difficult or impossible to follow the rule above regarding inclusion of archive headers beforeBOOST_CLASS_EXPORT
is invoked. This can best be addressed by usingBOOST_CLASS_EXPORT_KEY
in the header declarations andBOOST_CLASS_EXPORT_IMPLEMENT
in the class definition file.
[...片段...]
[ ... snip ... ]
将 BOOST_CLASS_EXPORT
放置在库代码中将无效,除非还包括存档类标头.因此,在构建库时,应包括他预期使用的所有存档类的所有标头.或者,可以仅包含Polymoprhic [sic] 档案的标头.
Placing BOOST_CLASS_EXPORT
in library code will have no effect unless archive class headers are also included. So when building a library, one should include all headers for all the archive classes which he anticipates using. Alternatively, one can include headers for just the Polymoprhic [sic] Archives.
查看 在魔盒上直播
-
main.cpp
main.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/nvp.hpp>
//#include <boost/serialization/export.hpp>
#include "baseclass.h"
#include "derivedclass.h"
int main() {
BaseClass* testBase = new BaseClass("regular stuff");
BaseClass* testDerived = new DerivedClass("special cool stuff");
std::cout << std::endl << " --- " << "Storing objects in the file 'output'..." << " --- " << std::endl;
{
std::ofstream output("storage");
boost::archive::text_oarchive boost_out(output);
boost_out << testBase << testDerived;
}
std::cout << std::endl << " --- " << "Storage done, now loading the first object as BaseClass" << " --- " << std::endl;
{
std::ifstream input("storage");
BaseClass* b1;
BaseClass* b2;
boost::archive::text_iarchive boost_in(input);
boost_in >> b1 >> b2;
std::cout << "b1: "; b1->printData();
std::cout << "b2: "; b2->printData();
}
}
baseclass.h
baseclass.h
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/access.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/export.hpp>
class BaseClass
{
public:
BaseClass() {
name = "BaseClass";
}
BaseClass(std::string custom) {
name = custom;
}
virtual ~BaseClass() {}
virtual void printData() {
std::cout << typeid(*this).name() << ": " << name << std::endl;
}
protected:
std::string name;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & (name);
}
};
BOOST_CLASS_EXPORT_KEY2(BaseClass, "BaseClass");
baseclass.cpp
baseclass.cpp
#include "baseclass.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(BaseClass)
derivedclass.h
derivedclass.h
#pragma once
#include <string>
#include <iostream>
#include <typeinfo>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/export.hpp>
#include "baseclass.h"
class DerivedClass : public BaseClass
{
public:
DerivedClass() : BaseClass("DerivedClass") {}
DerivedClass(std::string custom) : BaseClass("DerivedClass") {
data = custom;
}
virtual ~DerivedClass() {}
void printData() override {
std::cout << typeid(*this).name() << ": " << name << " AND " << data << std::endl;
}
protected:
std::string data;
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, unsigned) {
ar & (boost::serialization::base_object<BaseClass>(*this));
ar & (data);
}
};
BOOST_CLASS_EXPORT_KEY2(DerivedClass, "DerivedClass");
derivedclass.cpp
derivedclass.cpp
#include "derivedclass.h"
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
BOOST_CLASS_EXPORT_IMPLEMENT(DerivedClass)
输出:
这篇关于加速序列化,按基本类型加载存档的类会产生错误的数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!