【问题标题】:Boost Binary Deserialization提升二进制反序列化
【发布时间】:2021-12-03 22:14:35
【问题描述】:

我创建一个 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++ serialization boost binary deserialization


    【解决方案1】:

    当你保存时,你只写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&lt;&lt;,它必须使用operator&amp;,否则它只能用于保存,不能用于加载。您的编译器会很清楚地告诉您,您的代码与您发布的不同。

    现场观看: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              ...........
    

    【讨论】:

    • 我误解了如何使用 > vs & 运算符加载和保存数据。该代码与发布的代码完全相同,没有编译器警告。有点希望它给了我一个来查明错误。我从阅读文档中想到,我需要专门放置加载数据的位置。我只认为保存函数会自动调用序列化而不是加载函数。听起来这两个函数都会调用序列化,这对于我为什么要使用 & 而不是特定的 > 是有道理的。我很感激反馈。这似乎已经解决了它!!!
    • 啊。当然,由于您实际上并未在加载时调用Character 的序列化,因此不会导致编译器错误。无论如何,通常值得让您的样本自成一体(这是可能的,正如我的答案所示)。但听起来你现在掌握了 Boost Serialization 的窍门 :) 干杯
    猜你喜欢
    • 2014-01-04
    • 2011-09-26
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-31
    • 1970-01-01
    相关资源
    最近更新 更多