首页 > 解决方案 > 提升二进制反序列化

问题描述

我正在创建一个有趣的 D&D 引擎,只是为了练习我的 c++ 技能并学习一些更深入的主题。目前,我正在构建一个系统来保存和加载角色。我有一个 Stats 类,它包含一个角色的所有统计信息,以及一个当前只有一个名称和一个 stats* 到该角色的 stats 对象的角色类。

到目前为止,我已经能够使用 boost 文本存档成功保存数据,现在切换到 boost 二进制存档。保存数据时它似乎可以工作,但是当我尝试加载数据时出现此错误:

“异常未处理 - VileEngine.exe Microsoft C++ 异常中 [内存地址] 处的未处理异常:内存位置 [不同内存地址] 处的 boost::archive::archive_exception”

我可以多次跳过此错误,但是当程序运行和加载时,加载字符的数据已经偏离,所以我知道它必须是我保存它的方式,或者更有可能是我的方式我正在加载它。我已经尝试通读 boost 文档,但找不到修复它的方法。我也尝试搜索其他帖子但找不到答案,或者我只是不明白答案。任何帮助是极大的赞赏。

相关代码贴在下面。如果需要,我可以发布所有代码,但对于所有课程来说都是相当多的。

在 Character.hpp

    private:
        friend class boost::serialization::access; //allows serialization saving

        //creates the template class used by boost to serialize the classes data
        //serialize is call whenever this class is attempting to be saved
        template<class Archive>
        void serialize(Archive& ar, const unsigned int version) {
            ar << name;
            ar << *charStats;
            ar << inventory;
        }


/*********************************
*       Data Members
***********************************/

        std::string name;
        Stats* charStats;
        std::vector<std::string> inventory;

    public:
        Character();


        void loadCharacter(std::string &charName); //saves all character details
        void saveCharacter(); //loads all character details

在 Character.cpp 中

/*********************************************
Functions to save and load character details
**********************************************/

void Character::saveCharacter() {
    //save all details of character to charactername.dat file
    
    //create filename of format "CharacterName.dat"
    std::string fileName = name + ".dat";
    std::ofstream saveFile(fileName);

    //create serialized archive and save this characters data
    boost::archive::binary_oarchive outputArchive(saveFile);
    outputArchive << this;

    saveFile.close();
        
}

void Character::loadCharacter(std::string &charName) {
    //load details of .dat file into character using the characters name
    std::string fileName = charName + ".dat";
    std::ifstream loadFile(fileName);

    boost::archive::binary_iarchive inputArchive(loadFile);
    inputArchive >> name;

    Stats* temp = new Stats;
    inputArchive >> temp;
    charStats = temp;


    inputArchive >> inventory;

    loadFile.close();

}

在 Stats.hpp

private:

    friend class boost::serialization::access; //allows serialization saving

    //creates the template class used by boost to serialize the classes data
    //serialize is call whenever this class is attempting to be saved
    template<class Archive>
    void serialize(Archive& ar, const unsigned int version) {
        ar & skillSet;
        ar & subSkillMap;
        ar & level;
        ar & proficiencyBonus;
    }

标签: c++serializationboostbinarydeserialization

解决方案


当你保存时,你只写this(通过指针,这是一个错误,见下文):

boost::archive::binary_oarchive outputArchive(saveFile);
outputArchive << this;

当你加载时,你以某种方式阅读了三个不同的东西。为什么?他们显然应该匹配。和 100%。所以:

void Character::saveCharacter() {
    std::ofstream saveFile(name + ".dat");
    boost::archive::binary_oarchive outputArchive(saveFile);
    outputArchive << *this;
}

您保存*this(通过引用)是因为您不希望反序列化在堆上分配 Character 的新实例。如果这样做,则不能使其成为成员函数。

无论如何,您的序列化函数使用operator<<它必须使用的地方,operator&否则它只会用于保存,而不是加载。您的编译器会很清楚地告诉您,您的代码与您发布的不同。

现场观看:Live On Coliru

#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/set.hpp>
#include <boost/serialization/map.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/string.hpp>
#include <fstream>

struct Stats{
  private:
    std::set<int>              skillSet{1, 2, 3};
    std::map<int, std::string> subSkillMap{
        {1, "one"},
        {2, "two"},
        {3, "three"},
    };
    int    level            = 13;
    double proficiencyBonus = 0;

    friend class boost::serialization::access; //allows serialization saving

    template <class Archive> void serialize(Archive& ar, unsigned)
    {
        ar & skillSet;
        ar & subSkillMap;
        ar & level;
        ar & proficiencyBonus;
    }
};

struct Character {
  private:
    friend class boost::serialization::access; // allows serialization saving

    template <class Archive>
    void serialize(Archive& ar, const unsigned int version)
    {
        ar & name;
        ar & *charStats;
        ar & inventory;
    }

    /*********************************
     *       Data Members
     *********************************/

    std::string              name;
    Stats*                   charStats = new Stats{};
    std::vector<std::string> inventory;

  public:
    Character(std::string name = "unnamed") : name(std::move(name)){}
    ~Character() { delete charStats; }

    // rule of three (suggest to use no raw pointers!)
    Character(Character const&) = delete;
    Character& operator=(Character const&) = delete;

    void loadCharacter(std::string const& charName);
    void saveCharacter();
};

/*********************************************
Functions to save and load character details
**********************************************/

void Character::saveCharacter() {
    std::ofstream saveFile(name + ".dat");
    boost::archive::binary_oarchive outputArchive(saveFile);
    outputArchive << *this;
}

void Character::loadCharacter(std::string const &charName) {
    std::ifstream loadFile(charName + ".dat");
    boost::archive::binary_iarchive inputArchive(loadFile);
    inputArchive >> *this;
    loadFile.close();
}

int main() {
    {
        Character charlie { "Charlie" }, bokimov { "Bokimov" };

        charlie.saveCharacter();
        bokimov.saveCharacter();
    }

    {
        Character someone, someone_else;
        someone.loadCharacter("Charlie");
        someone_else.loadCharacter("Bokimov");
    }
}

保存两个文件并重新加载它们:

==== Bokimov.dat ====
00000000: 1600 0000 0000 0000 7365 7269 616c 697a  ........serializ
00000010: 6174 696f 6e3a 3a61 7263 6869 7665 1300  ation::archive..
00000020: 0408 0408 0100 0000 0000 0000 0007 0000  ................
00000030: 0000 0000 0042 6f6b 696d 6f76 0000 0000  .....Bokimov....
00000040: 0003 0000 0000 0000 0000 0000 0001 0000  ................
00000050: 0002 0000 0003 0000 0000 0000 0000 0300  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000070: 0000 0003 0000 0000 0000 006f 6e65 0200  ...........one..
00000080: 0000 0300 0000 0000 0000 7477 6f03 0000  ..........two...
00000090: 0005 0000 0000 0000 0074 6872 6565 0d00  .........three..
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 00              ...........
==== Charlie.dat ====
00000000: 1600 0000 0000 0000 7365 7269 616c 697a  ........serializ
00000010: 6174 696f 6e3a 3a61 7263 6869 7665 1300  ation::archive..
00000020: 0408 0408 0100 0000 0000 0000 0007 0000  ................
00000030: 0000 0000 0043 6861 726c 6965 0000 0000  .....Charlie....
00000040: 0003 0000 0000 0000 0000 0000 0001 0000  ................
00000050: 0002 0000 0003 0000 0000 0000 0000 0300  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0001  ................
00000070: 0000 0003 0000 0000 0000 006f 6e65 0200  ...........one..
00000080: 0000 0300 0000 0000 0000 7477 6f03 0000  ..........two...
00000090: 0005 0000 0000 0000 0074 6872 6565 0d00  .........three..
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 00              ...........

推荐阅读