Boost接口映射的序列化
我创建了多种不同数据类型的地图。 s64、f64、数组、图像等
为此,我使用了 std::map
。
我想存储它,并从文件系统重新加载它。但我听说地图不能用一句话来表达。所以我尝试存储数据部分 std::unique_ptr
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, unsigned int version) {
ar & test;
}
程序在 ar & 处崩溃。测试抛出异常:“未注册的类 - 未注册或导出的派生类”
。
有什么问题吗?我不明白。
这是最小的代码:(已删除)
#include <boost/serialization/vector.hpp>
//....
};
正如 463035818_is_not_a_number 指出的那样,我的狙击不起作用。
我重新创建了它,并且我认为得到了更多。但是一旦我插入从文件加载函数,它就不再编译了:
错误 C2280:“std::pair
#include <boost/serialization/vector.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/utility.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <iostream>
class MapEntryInterface {
public:
MapEntryInterface(std::string type_str) : type(type_str) {}
std::string type;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, unsigned int version) {
if (type == "s64") {
MapEntryS64* cast = (MapEntryS64*)this;
cast->serialize(ar, version);
}
if (type == "f64") {
MapEntryF64* cast = (MapEntryF64*)this;
cast->serialize(ar, version);
}
}
};
class MapEntryS64 : public MapEntryInterface {
public:
MapEntryS64(int init_val, const std::string& type_str)
: data(init_val), MapEntryInterface(type_str)
{}
uint64_t data;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, unsigned int version) {
ar & type;
ar & data;
}
};
class MapEntryF64 : public MapEntryInterface {
public:
MapEntryF64(double init_val, const std::string& type_str)
: data(init_val), MapEntryInterface(type_str)
{}
double data;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, unsigned int version) {
ar & type;
ar & data;
}
};
class MapDataBase {
public:
MapDataBase()
//: test(std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64")))
{
database["key1"] = std::unique_ptr<MapEntryInterface>(new MapEntryS64(381, "s64"));
database["key2"] = std::unique_ptr<MapEntryInterface>(new MapEntryF64(3.124512, "f64"));
};
bool SaveToFile() {
std::ofstream ofs("boost_export.dat");
if (ofs.is_open()) {
boost::archive::text_oarchive oa(ofs);
oa & *this;
return true;
}
return false;
}
bool loadFromFile() {
std::ifstream ifs("boost_export.dat");
if (ifs.is_open())
{
try
{
boost::archive::text_iarchive ia(ifs);
ia & *this;
//std::string yolo;
//ia >> yolo;
//ia >> bam;
}
catch (std::exception& ex)
{
std::cout << ex.what() << std::endl;
return false;
}
}
return true;
}
private:
std::map<std::string, std::unique_ptr<MapEntryInterface>> database;
//std::unique_ptr<MapEntryInterface> test;
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, unsigned int version) {
ar & database;
}
};
void main() {
MapDataBase tmp;
tmp.SaveToFile();
MapDataBase tmp2;
//tmp2.loadFromFile();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我花了很多时间让事情变得独立。在众多更改中:
您不应该让基类序列化派生类(这是经典的 OOP),相反,Boost 希望派生类序列化它们的
base_object
(允许静态多态性,顺便说一句,输入注册)当然,基类应该序列化其数据成员(
类型
)基类应该有一个虚拟析构函数(否则通过
unique_ptr删除 的析构函数将是未指定的
<块引用>
直到这里:
基本/成员初始化列表的顺序具有误导性;无论如何,初始化都是按照声明顺序进行的
type_str
是一种代码味道:OOP 虚拟化的整个思想是不在任何地方都可以切换类型。我至少通过默认值来为您做了一半的工作,但您可能可以完全不使用它。毕竟类型就是类型。现在添加
base_object
序列化:MapDatabase
受益于多项简化new
或delete
loadFromFile
没有有用的方法来处理异常、重新抛出或只是让其转义,MapDatabase
设置为返回类型,而不是bool
。saveToFile
和loadFromFile
可能应该采用文件名参数未显示:可以说
saveToFile
和loadFromFile
不需要MapDatabase
的一部分此时,添加一点代码来打印数据库内容:
< strong>在 Wandbox 上直播
打印
unregistered class
运行时错误该消息说明了一切:在反序列化时,没有有关可以反序列化的类型的信息。添加:
现在它会打印
几个翻译单元。
如果是单独的翻译单元,请根据文档拆分类导出宏。例如,类导出宏最终会
天真地这样做(又花了半个小时来明智地拆分所有操作:)
File
test.cpp
<前><代码>#include“MapDatabase.h”;
#include
int main() {
{
地图数据库tmp;
std::cout << "------ tmp:\n" << tmp << “\n”;
if (不是 tmp.SaveToFile("boost_export.dat"))
返回1;
}
MapDatabase 往返 = MapDatabase::loadFromFile("boost_export.dat");
std::cout << "-------- 往返:\n" <<往返<< “\n”;
}
文件
MapDatabase.h
文件
MapDatabase.cpp
<前><代码>#include“MapDatabase.h”;;;;
#include
#include
#include
#include
地图数据库::地图数据库() {(381));(3.124512));
database_.emplace("key1", std::make_unique
database_.emplace("key2", std::make_unique
}
bool MapDatabase::SaveToFile(std::string const& 文件名) const
{
尝试 {
std::ofstream ofs(文件名, std::ios::binary);
boost::archive::text_oarchive oa(ofs);
OA << *这;
返回真;
} catch (std::Exception& ex) {
std::cout <<例如.what() << std::endl;
返回假;
}
}
MapDatabase MapDatabase::loadFromFile(std::string const& 文件名)
{
地图数据库数据库;
std::ifstream ifs(文件名, std::ios::binary);
boost::archive::text_iarchive ia(ifs);
ia>>数据库;
返回数据库;
}
std::ostream&运算符<<(std::ostream&os,MapDatabase const&mdb)
{
for (auto const& [k, b] : mdb.database_)
如果 (b) os << std::quoted(k) << “->”<< *b<<; “\n”;
否则操作系统<< std::quoted(k) << " -> NULL\n";
返回操作系统;
}
文件
MapEntryF64.h
文件
MapEntryInterface.h
文件
MapEntryS64.h
文件
MapEntryF64.cpp
<前><代码>#include“MapEntryF64.h”;;
#include
#include
MapEntryF64::MapEntryF64(int init_val, const std::string& type_str)
: MapEntryInterface(type_str)
, 数据_(初始化值)
{
}
void MapEntryF64::print(std::ostream&os) const {
操作系统<< "F64(" << data_ << ", " << std::quoted(type_) << ")";
}
BOOST_CLASS_EXPORT_IMPLMENT(MapEntryF64)
文件
MapEntryInterface.cpp
<前><代码>#include“MapEntryInterface.h”
MapEntryInterface::MapEntryInterface(std::string type_str) : type_(type_str) {}
std::ostream&运算符<<(std::ostream&os,MapEntryInterface const&e)
{
e.print(os);
返回操作系统;
}
文件
MapEntryS64.cpp
<前><代码>#include“MapEntryS64.h”;;
#include
#include
MapEntryS64::MapEntryS64(int init_val, const std::string& type_str)
: MapEntryInterface(type_str)
, 数据_(初始化值)
{
}
void MapEntryS64::print(std::ostream&os) const {
操作系统<< "S64(" << data_ << ", " << std::quoted(type_) << ")";
}
BOOST_CLASS_EXPORT_IMPLMENT(MapEntryS64)
打印:Live On Wandbox
呃我们注定要失败吗?
一点也不。只需要 仔细阅读文档:
因此,添加以下内容:(
对于
MapEntryS64
也是如此):Live On Wandbox
奖励:简单
我更喜欢简单。我可能会将以上所有内容替换为:
File
MapDatabase.h
文件
MapDatabase.cpp
<前><代码>#include“MapDatabase.h”;;;;;;;
#include
#include
#include
#include
#include
#include
#include
命名空间数据库{
std::string_view typeOf(Entry const& e) {
断言(e.which() < 3);
返回 std::array{"Nil", "S64", "F64"}[e.which()];
}
void SaveToFile(std::string const& 文件名, Map const& m)
{
std::ofstream ofs(文件名, std::ios::binary);
boost::archive::text_oarchive oa(ofs);
OA <<米;
}
映射 loadFromFile(std::string const& 文件名)
{
地图数据库;
std::ifstream ifs(文件名, std::ios::binary);
boost::archive::text_iarchive ia(ifs);
ia>>数据库;
返回数据库;
}
std::ostream&运算符 <<(std::ostream&os, Nil) { return os << “无效的”; }
std::ostream&运算符<<(std::ostream&os,Map const&m)
{
for (auto const& [k, v] : m)
操作系统<< typeOf(v) << “\t”<< std::quoted(k) << “->”<< v << “\n”;
返回操作系统;
}
} // 命名空间数据库
文件
test.cpp
<前><代码>#include“MapDatabase.h”;
#include
int main() {
SaveToFile("boost_export.dat",
数据库::地图{
{“key1”,381ul},
{“key3”,{}},
{“键2”,3.124512},
});
std::cout <<数据库::loadFromFile("boost_export.dat");
}
打印 Live On Wandbox
I spent a large amount of time making things self-contained. Among the many changes:
you should not have the base class serializing the derived (that's classic OOP), instead Boost expects derived classes to serialize their
base_object<>
(allowing static polymorphism and, incidentally, type registration)of course, the base class should serialize its data members (
type
)the base class SHOULD have a virtual destructor (otherwise deleting through
unique_ptr
's destructor will be unspecifiedThe base/member initialize lists are in misleading order; initialization happens in declaration order anyways
The
type_str
is a code smell: the whole idea of OOP virtualization is not to have switching on types everywhere. I went half-way for you by at least defaulting the values, but you could probably do without it entirely. After all the type is the type.Now add the
base_object
serialization:MapDatabase
benefits from several simplificationsnew
ordelete
loadFromFile
has no useful way of handling the exception, re-throw, or just let is escape,MapDatabase
the return type instead ofbool
.saveToFile
andloadFromFile
should probably take a filename parameterNot shown: arguably
saveToFile
andloadFromFile
need not be part ofMapDatabase
At this point, adding a little bit of code to print database contents:
Live On Wandbox
Prints
unregistered class
runtime errorThe message says it all: at time of deserialization, there is no information about the types that can be deserialized. Add that:
Now it prints
Several Translation Units
In the case of separate translation units, split the class export macros as per the documentation. E.g. the class export macro ends up doing
So, naively (taking another half-hour to split it all op sensibly:)
File
test.cpp
File
MapDatabase.h
File
MapDatabase.cpp
File
MapEntryF64.h
File
MapEntryInterface.h
File
MapEntryS64.h
File
MapEntryF64.cpp
File
MapEntryInterface.cpp
File
MapEntryS64.cpp
Prints: Live On Wandbox
UHOH Are We Doomed?
Not at all. Just need to read the documentation closely:
So, adding the includes:
(and the same for
MapEntryS64
):Live On Wandbox
BONUS: Simplicity
I prefer simplicity. I would probably replace all of the above with:
File
MapDatabase.h
File
MapDatabase.cpp
File
test.cpp
Printing Live On Wandbox