提问人:Panagiotis Foliadis 提问时间:10/28/2023 最后编辑:Panagiotis Foliadis 更新时间:11/2/2023 访问量:68
如何序列化/反序列化派生类的unordered_map成员
How to Serialize/Deserialize an unordered_map member of a derived Class
问:
因此,我正在用 C++ 构建一个模拟文件系统,以更好地研究该语言,也许还有一些系统级编程。当用户退出时,我使用 Boost::Serialization 保存文件系统的状态,但我在保存/加载类时遇到了问题。 这是我的基类:
enum filetype { FSFILE, FSDIRECTORY, ROOT };
class FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_NVP(name);
ar & BOOST_SERIALIZATION_NVP(date_of_creation);
ar & BOOST_SERIALIZATION_NVP(type);
}
protected:
std::string name;
std::string date_of_creation;
filetype type;
这是我的第一个派生类,它基本上是系统中的一个文件:.txt
class File : public FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject);
ar & BOOST_SERIALIZATION_NVP(content);
ar & BOOST_SERIALIZATION_NVP(size);
}
protected:
std::string content;
int size;
最后,这里是将充当保存文件和/或其他目录的目录的类:Directory
class Directory : public FileObject {
private:
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int) {
ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject);
ar & BOOST_SERIALIZATION_NVP(num_of_contents);
ar & BOOST_SERIALIZATION_NVP(size_of_contents);
for (auto it = this->contents.begin(); it != this->contents.end(); it++) {
ar & BOOST_SERIALIZATION_NVP(it->second);
}
}
protected:
int num_of_contents;
int size_of_contents;
public:
std::unordered_map<std::string, FileObject *> contents;
在我的 main.cc 文件中,我有 2 个函数,一个用于保存,一个用于加载
void save_state(const Directory &s, const char * filename){
// make an archive
std::ofstream ofs(filename);
assert(ofs.good());
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_SERIALIZATION_NVP(s);
}
void
restore_state(Directory &s, const char * filename)
{
// open the archive
std::ifstream ifs(filename);
assert(ifs.good());
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_SERIALIZATION_NVP(s);
}
最后,这里是 rest 文件,它创建了一些文件和目录,并保存/加载用于测试目的:main.cc
int main() {
char c;
scanf("%c", &c);
std::string filename(boost::archive::tmpdir());
filename += "/demo_save.xml";
if (c == 's') {
File file("test_file1", filetype::FSFILE);
File file2("test_file2", filetype::FSFILE);
File file3("test_file3", filetype::FSFILE);
File file4("test_file4", filetype::FSFILE);
Directory dir("test_dir1", filetype::FSDIRECTORY);
Directory dir2("test_dir2", filetype::FSDIRECTORY);
dir.insertContent(file.getName(), &file);
dir.insertContent(file2.getName(), &file2);
dir2.insertContent(file3.getName(), &file3);
dir2.insertContent(file4.getName(), &file4);
dir.insertContent(dir2.getName(), &dir2);
save_state(dir, filename.c_str());
}
Directory newd;
if (c == 'l') {
restore_state(newd, filename.c_str());
for (auto it = newd.contents.begin(); it != newd.contents.end(); it++) {
if (it->second->getType() == filetype::FSFILE) {
std::cout << it->first << std::endl;
}
else if (it->second->getType() == filetype::FSDIRECTORY) {
for (auto jt = ((Directory *)it->second)->contents.begin(); jt != ((Directory *)it->second)->contents.end(); jt++) {
std::cout << jt->second->getName() << std::endl;
}
}
}
}
return 0;
}
程序编译良好,但我在第二个循环中出现 seg 错误。通过读取文件,其中的文件没有得到正确的序列化。.xml
dir2
我的类和函数是否正确?这是序列化包含指向其他类的指针的正确方法吗?unordered_map
答:
1赞
sehe
10/28/2023
#1
正如其他人所指出的,你有所有权问题。可以序列化指针,但反序列化将导致内存泄漏。
相反,使指针拥有。我将用它来管理它,而不是编写大量代码来正确管理生命周期。unique_ptr
std::unordered_map<std::string, std::unique_ptr<FileObject> > contents;
然后,您必须确保层次结构是虚拟的,至少添加
virtual ~FileObject() = default;
我选择通过提供虚拟方法来使流式处理成为可流式处理。FileObject
print
最后,注册类型,或根据需要将它们标记为抽象:
BOOST_SERIALIZATION_ASSUME_ABSTRACT(FileObject)
BOOST_CLASS_EXPORT(File)
BOOST_CLASS_EXPORT(Directory)
完整演示
#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/nvp.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/unordered_map.hpp>
#include <filesystem>
#include <iostream>
#include <set>
namespace fs = std::filesystem;
enum filetype { FSFILE, FSDIRECTORY, ROOT };
class FileObject {
public:
std::string getName() const { return name; }
virtual ~FileObject() = default;
virtual void print(std::ostream& os, std::string const& prefix = "") const {
os << "[" << prefix << "/]" << getName() << std::endl;
}
private:
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_NVP(name) & BOOST_NVP(date_of_creation) & BOOST_NVP(type);
}
protected:
FileObject() = default; // only for deserialization
FileObject(std::string name, filetype type) : name(std::move(name)), type(type) {
// TODO date_of_creation
}
std::string name;
std::string date_of_creation;
filetype type = ROOT;
friend std::ostream& operator<<(std::ostream& os, FileObject const& fo) {
fo.print(os);
return os;
}
};
class File : public FileObject {
public:
File(std::string name, size_t size) : FileObject(std::move(name), FSFILE), size(size) {}
private:
File() = default; // only for deserialization
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject) & BOOST_NVP(content) & BOOST_NVP(size);
}
protected:
std::string content;
size_t size = 0;
};
class Directory : public FileObject {
public:
Directory() : FileObject("/", ROOT) {}
Directory(std::string name, filetype type = FSDIRECTORY) : FileObject(name, type) {
assert(FSDIRECTORY == type);
}
bool insertContent(std::unique_ptr<FileObject> object) {
std::string name = object->getName();
auto [it, ok] = contents.emplace(std::move(name), std::move(object));
if (ok)
num_of_contents += 1; // TODO size_of_contents?
assert(contents.size() == num_of_contents);
return ok;
}
private:
std::unordered_map<std::string, std::unique_ptr<FileObject> > contents;
friend class boost::serialization::access;
template <class Ar> void serialize(Ar& ar, unsigned) {
ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(FileObject) & BOOST_NVP(num_of_contents) &
BOOST_NVP(size_of_contents) & BOOST_NVP(contents);
}
protected:
size_t num_of_contents = 0;
size_t size_of_contents = 0;
virtual void print(std::ostream& os, std::string const& prefix) const override {
FileObject::print(os, prefix);
for (auto const& [n, obj] : contents)
if (obj)
obj->print(os, prefix + "/" + getName());
}
};
BOOST_SERIALIZATION_ASSUME_ABSTRACT(FileObject)
BOOST_CLASS_EXPORT(File)
BOOST_CLASS_EXPORT(Directory)
#include <fstream>
static inline void save_state(Directory const& s, fs::path const& filename) {
std::ofstream ofs(filename);
boost::archive::xml_oarchive oa(ofs);
oa << BOOST_NVP(s);
}
static inline void restore_state(Directory& s, fs::path const& filename) {
std::ifstream ifs(filename);
boost::archive::xml_iarchive ia(ifs);
ia >> BOOST_NVP(s);
}
int main(int argc, char** argv) {
std::set<std::string_view> const args(argv + 1, argv + argc);
auto filename = fs::temp_directory_path() / "demo_save.xml";
if (args.contains("save")) {
Directory dir("test_dir1", filetype::FSDIRECTORY);
for (auto name : {"test_file1", "test_file2", "test_file3", "test_file4"})
dir.insertContent(std::make_unique<File>(name, filetype::FSFILE));
dir.insertContent(std::make_unique<Directory>("test_dir2"));
save_state(dir, filename);
}
if (args.contains("load")) {
Directory newd;
restore_state(newd, filename);
std::cout << newd;
}
}
指纹
[/]test_dir1
[/test_dir1/]test_file1
[/test_dir1/]test_file2
[/test_dir1/]test_file3
[/test_dir1/]test_dir2
[/test_dir1/]test_file4
xml 包含:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="19">
<s class_id="0" tracking_level="1" version="0" object_id="_0">
<FileObject class_id="1" tracking_level="1" version="0" object_id="_1">
<name>test_dir1</name>
<date_of_creation></date_of_creation>
<type>1</type>
</FileObject>
<num_of_contents>5</num_of_contents>
<size_of_contents>0</size_of_contents>
<contents class_id="2" tracking_level="0" version="0">
<count>5</count>
<bucket_count>13</bucket_count>
<item_version>0</item_version>
<item class_id="3" tracking_level="0" version="0">
<first>test_file4</first>
<second class_id="4" tracking_level="0" version="0">
<tx class_id="5" class_name="File" tracking_level="1" version="0" object_id="_2">
<FileObject object_id="_3">
<name>test_file4</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_dir2</first>
<second>
<tx class_id_reference="0" object_id="_4">
<FileObject object_id="_5">
<name>test_dir2</name>
<date_of_creation></date_of_creation>
<type>1</type>
</FileObject>
<num_of_contents>0</num_of_contents>
<size_of_contents>0</size_of_contents>
<contents>
<count>0</count>
<bucket_count>1</bucket_count>
<item_version>0</item_version>
</contents>
</tx>
</second>
</item>
<item>
<first>test_file3</first>
<second>
<tx class_id_reference="5" object_id="_6">
<FileObject object_id="_7">
<name>test_file3</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_file2</first>
<second>
<tx class_id_reference="5" object_id="_8">
<FileObject object_id="_9">
<name>test_file2</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
<item>
<first>test_file1</first>
<second>
<tx class_id_reference="5" object_id="_10">
<FileObject object_id="_11">
<name>test_file1</name>
<date_of_creation></date_of_creation>
<type>0</type>
</FileObject>
<content></content>
<size>0</size>
</tx>
</second>
</item>
</contents>
</s>
</boost_serialization>
评论
0赞
Panagiotis Foliadis
11/2/2023
如果我们在将 dir2 插入 dir1 之前在 dir2 上插入一些文件,这会起作用吗?
0赞
sehe
11/2/2023
当然,为什么不呢?你试过吗?coliru.stacked-crooked.com/a/79df4ada3044682e下面是一个遍历实际文件系统文件夹的演示: coliru.stacked-crooked.com/a/5e0948e8ff90df6c
0赞
Panagiotis Foliadis
11/2/2023
我试过了,但没有用。我研究演示,非常感谢!
评论
ar & BOOST_SERIALIZATION_NVP(it->second);
if (c == 's')
File
File
Directory