Search code examples
c++sdl

Saving vector of objects using SDL2


Im learning SDL2 and now im trying to save a game ranking into a .bin file. I have placed the ranking data into a vector. But im unable to save the data. Perhabs it has to do with file size, but im unable to solve that. This is the code that im using now:

class Player {
 private:
  std::string name, faction, dific;
  int points;
 public:
  Player() {};
  virtual ~Player() {};
  void addName(std::string s);
  void addFacti(std::string s);
  void addDific(std::string s);
  void addPoint(int p);
  std::string getName() const;
  std::string getFacti() const;
  std::string getDific() const;
  int getPoint() const;
  bool operator>(const Player& p) const;
};

Player p1; Player p2;//just two examples

//add properties to each object

std::vector<Player>classi;
classi.push_back(p1); classi.push_back(p2);
std::sort(classi.begin(), classi.end(), std::greater<Player>());

//load file
SDL_RWops* rankfile = SDL_RWFromFile("ranking.bin", "r+b");
for (int i = 0; i < classi.size(); ++i) { SDL_RWread(rankfile, &classi[i], sizeof(Player), 1); }
SDL_RWclose(rankfile);

//im able to render the objects

//save file - but it doesnt save anything
rankfile = SDL_RWFromFile("ranking.bin", "w+b");
for (int i = 0; i < classi.size(); ++i) { SDL_RWwrite(rankfile, &classi[i], sizeof(Player), 1); }
SDL_RWclose(rankfile);

Solution

  • Here's an example of how I might serialize a struct containing std::string to/from binary. It doesn't use the SDL functions because I don't use SDL but it wouldn't be difficult to modify if desired.

    You may have a different problem since you say your file is empty, and I am concerned that you try and read the file before you write it, but you could try something like this and see if it helps.

    #include <iostream>
    #include <string>
    #include <fstream>
    #include <tuple>
    
    template <typename T>
    std::ostream &writeBinary(std::ostream &f, T data)
    {
        return f.write(reinterpret_cast<char *>(&data), sizeof(data));
    }
    
    std::ostream &writeBinary(std::ostream &f, const std::string &str)
    {
        // If file size is a concern you might use a smaller type like uint16_t.
        // Just make sure to mirror the change in readBinary.
        std::string::size_type sz = str.size();
        if (f)
        {
            f.write(reinterpret_cast<char *>(&sz), sizeof(sz));
        }
        if (f)
        {
            f.write(str.data(), str.size());
        }
        return f;
    }
    
    template <typename T>
    std::istream &readBinary(std::istream &f, T &data)
    {
        if (f)
        {
            f.read(reinterpret_cast<char *>(&data), sizeof(data));
        }
        return f;
    }
    
    std::istream &readBinary(std::istream &f, std::string &str)
    {
        std::string::size_type sz = 0;
        if (f)
        {
            f.read(reinterpret_cast<char *>(&sz), sizeof(sz));
        }
        if (f)
        {
            str.resize(sz);
            f.read(str.data(), sz);
        }
        return f;
    }
    
    struct Thing
    {
        std::string shortString;
        int i;
        double d;
        std::string longString;
    
        Thing()
            : i(99)
            , d(99.99)
        {    }
    
        bool operator==(const Thing &rhs) const
        {
            return std::tie(shortString, i, d, longString)
                == std::tie(rhs.shortString, rhs.i, rhs.d, rhs.longString);
        }
    
        bool write(std::ofstream &f)
        {
            if (!writeBinary(f, shortString))
            {
                return false;
            }
            if (!writeBinary(f, i))
            {
                return false;
            }
            if (!writeBinary(f, d))
            {
                return false;
            }
            if (!writeBinary(f, longString))
            {
                return false;
            }
            return true;
        }
    
        bool read(std::ifstream &f)
        {
            if (!readBinary(f, shortString))
            {
                return false;
            }
            if (!readBinary(f, i))
            {
                return false;
            }
            if (!readBinary(f, d))
            {
                return false;
            }
            if (!readBinary(f, longString))
            {
                return false;
            }
            return true;
        }
    };
    
    std::ostream &operator<<(std::ostream &o, const Thing &t)
    {
        return o << "'" << t.shortString << "'" << ", "
            << t.i << ", " << t.d << ", "
            << "'" << t.longString << "'";
    }
    
    int main()
    {
        Thing t1;
        // Shorter string to hopefully fit in any short string optimization buffer in the string. 
        t1.shortString = "Short";
        t1.longString = "Long string that should be long enough to not fit in the SSO buffer.";
        t1.i = 42;
        t1.d = 42.42;
    
        std::cout << "t1 Before Write: " << t1 << "\n";
        std::ofstream out("thing.bin", std::ios::binary);
        if (!t1.write(out))
        {
            std::cout << "Error writing t1!\n";
            return -1;
        }
        out.close();
        std::cout << "t1 After Write: " << t1 << "\n";
    
        Thing t2;
        std::cout << "t2 Before Read: " << t2 << "\n";
        std::ifstream in("thing.bin", std::ios::binary);
        if (!t2.read(in))
        {
            std::cout << "Error reading t2!\n";
            return -1;
        }
        in.close();
        std::cout << "t2 After Read: " << t2 << "\n";
    
        std::cout << "t1 == t2: " << std::boolalpha << (t1 == t2) << "\n";
    
        return 0;
    }