Home > OS >  Boost Binary Deserialization
Boost Binary Deserialization

Time:10-16

I'm creating a D&D engine for fun just to practice my c skills and learn some of the more in depth topics. Currently, I am working on building a system to save and load characters. I have a Stats class, that holds all of the statistics for a character, and a character class that currently just has a name and a stats* to a stats object for that character.

So far, I've been able to successfully save the data using boost text archive, and now switched to boost binary archive. It appears to work when saving the data, but when I try to load the data I get this error:

"Exception Unhandled - Unhandled exception at [memory address] in VileEngine.exe Microsoft C exception: boost::archive::archive_exception at memory location [different mem address]"

I can skip past this error multiple times but when the program runs and loads, the data of the loaded character is way off so I know it has to be either in the way I'm saving it, or more likely in the way I'm loading it. I've tried reading through the boost docs but couldn't find a way to fix it. I also tried searching through other posts but couldn't find an answer, or maybe I just don't understand the answers. Any help is greatly appreciated.

Relevant code posted below. I can post all the code if needed but it's quite a bit for all the classes.

in 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

in 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();

}

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

CodePudding user response:

When you save, you ONLY write this (by pointer, which is an error, see below):

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

Whe you load, you somehow read three separate things. Why? They should obviously match. And 100%. So:

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

You save *this (by reference) because you do not want the deserialization to allocate a new instance of Character on the heap. If you do, you cannot make it a member function.

Regardless, your serialize function uses operator<< where it MUST use operator& because otherwise it will only work for save, not load. Your compiler would have told you, so clearly, your code is different from what you posted.

See it live: 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");
    }
}

Saves two files and loads them back:

==== 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              ...........
  • Related