如何序列化/反序列化派生类的 unordered_map 成员

问题描述 投票:0回答:1

因此,我正在用 C++ 构建一个模拟文件系统,以更好地研究该语言,也许还可以进行一些系统级编程。我使用 Boost::Serialization 在用户退出时保存文件系统的状态,但我在保存/加载类时遇到问题。 这是我的基类:

enter code here

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);
}

最后这是其余的

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;
}

程序编译正常,但我在第二个循环中出现段错误。从读取

.xml
文件来看,
dir2
内的文件未正确序列化。

我的类和函数正确吗?这是序列化包含指向其他类的指针的

unordered_map
的正确方法吗?

c++ boost c++14 xml-serialization
1个回答
0
投票

正如其他人指出的那样,您存在所有权问题。您可以序列化指针,但反序列化会导致内存泄漏。

相反,让指针成为拥有的。我将使用

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)

完整演示

住在Coliru

#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();
        return contents.emplace(std::move(name), std::move(object)).second;
    }

  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:
    int num_of_contents;
    int size_of_contents;

    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>4199236</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>
© www.soinside.com 2019 - 2024. All rights reserved.