Skip to content

Instantly share code, notes, and snippets.

@ragecryx
Last active November 16, 2016 10:39
Show Gist options
  • Save ragecryx/a65d0893e3716f7506d800fce9b1fcb5 to your computer and use it in GitHub Desktop.
Save ragecryx/a65d0893e3716f7506d800fce9b1fcb5 to your computer and use it in GitHub Desktop.
// build!g++ -std=c++11 -g -Wall -o bin_serialization bin_serialization.cpp
// run!./bin_serialization
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <string>
#include <type_traits>
#include <sys/stat.h>
#include <iostream>
using namespace std;
/////////////////////////// Serializer Code ///////////////////////////
class Serializer;
class Serializable {
public:
virtual void SaveTo(Serializer *s) const = 0;
virtual void LoadFrom(Serializer *s) = 0;
};
// SFINAE
template<class T>
using IsScalar = typename std::enable_if< std::is_scalar<T>::value >::type*;
template<class T>
using IsSerializable = typename std::enable_if< std::is_base_of<Serializable, T>::value, Serializable >::type*;
template<class T>
using IsString = typename std::enable_if< std::is_base_of<std::string, T>::value, std::string >::type*;
class Serializer {
public:
Serializer() : mIsWriting(false), mpFile(nullptr) { }
~Serializer() {
if (mpFile != nullptr) {
fclose(mpFile);
mpFile = nullptr;
}
}
// * Writing methods...
template<class T, IsScalar<T> = nullptr>
void Write (const T& value) {
if (!mIsWriting) SwitchMode();
fwrite(&value, sizeof(T), 1, mpFile);
}
template<class T, IsSerializable<T> = nullptr>
void Write (const T& value) {
if (!mIsWriting) SwitchMode();
value.SaveTo(this);
}
template<class T, IsString<T> = nullptr>
void Write (const T& value) {
if (!mIsWriting) SwitchMode();
int size = value.size();
fwrite(&size, sizeof(int), 1, mpFile);
fwrite(value.c_str(), size, 1, mpFile);
}
// * Reading methods...
template<class T, IsScalar<T> = nullptr>
void Read (T &value) {
if (mIsWriting) SwitchMode();
fread(&value, sizeof(T), 1, mpFile);
}
template<class T, IsSerializable<T> = nullptr>
void Read (T &value) {
if (mIsWriting) SwitchMode();
value.LoadFrom(this);
}
template<class T, IsString<T> = nullptr>
void Read (T &value) {
if (mIsWriting) SwitchMode();
int size;
fread(&size, sizeof(int), 1, mpFile);
char* tmp = new char[size+1];
tmp[size] = '\0';
fread(tmp, size, 1, mpFile);
value.assign(tmp);
delete[] tmp;
}
// * File methods
void SetFile (const std::string filename) {
mFilename = filename;
if (mIsWriting)
BeginWriteMode();
else
BeginReadMode();
}
void Close () {
fclose(mpFile);
mpFile = nullptr;
}
private:
void BeginWriteMode () {
mIsWriting = true;
if ( mpFile != nullptr)
Close();
mpFile = fopen(mFilename.c_str(), "wb");
if (mpFile == nullptr)
std::cerr << "[Serializer] Cannot open " << mFilename << " for writing!" << std::endl;
}
void BeginReadMode () {
mIsWriting = false;
if ( mpFile != nullptr)
Close();
mpFile = fopen(mFilename.c_str(), "rb");
if (mpFile == nullptr)
std::cerr << "[Serializer] Cannot open " << mFilename << " for reading!" << std::endl;
}
void SwitchMode () {
if (mFilename.size() > 0) {
if (mIsWriting) {
BeginReadMode();
} else {
BeginWriteMode();
}
}
}
private:
std::string mFilename;
bool mIsWriting;
FILE* mpFile;
};
// EXAMPLE //////////////////////////////////////////////////
class CharacterPlayer : public Serializable {
public:
CharacterPlayer() : kills(0), xp(0), name() {}
~CharacterPlayer() {}
public:
void SaveTo(Serializer *s) const {
s->Write(xp);
s->Write(name);
}
void LoadFrom(Serializer *s) {
s->Read(xp);
s->Read(name);
}
public:
int kills;
float xp;
std::string name;
};
typedef struct _character_combat_stats {
unsigned int armorClass = 1;
unsigned int attackBase = 1;
float attackSpeed = 1;
} CharacterCombatStats;
class Character : public Serializable {
public:
Character() : health(10), speed(1.0f), player() {};
~Character() {};
public:
void SaveTo(Serializer *s) const {
s->Write(speed);
s->Write(combatStats.armorClass);
s->Write(combatStats.attackBase);
s->Write(combatStats.attackSpeed);
s->Write(player);
}
void LoadFrom(Serializer *s) {
s->Read(speed);
s->Read(combatStats.armorClass);
s->Read(combatStats.attackBase);
s->Read(combatStats.attackSpeed);
s->Read(player);
}
public:
int health;
float speed;
CharacterCombatStats combatStats;
CharacterPlayer player;
};
std::ostream &operator<<(std::ostream &os, Character const &a) {
os << "Character:\n" << " * Player: " << a.player.name << " (" << a.player.xp << "xp/" << a.player.kills << "k)" << endl;
os << " * Health: " << a.health << "\n * Speed: " << a.speed << endl;
os << " * Combat Stats ===\n" << " * AC ~ " << a.combatStats.armorClass << endl;
os << " * AtkBase ~ " << a.combatStats.attackBase << endl;
os << " * AtkSpeed ~ " << a.combatStats.attackSpeed << endl;
return os;
}
int main (int argc, char const *argv[]) {
cout << "* Creating Character..." << endl;
Character a;
a.health = 20;
a.speed = 12.3f;
a.combatStats.armorClass = 12;
a.combatStats.attackBase = 4;
a.combatStats.attackSpeed = 1.337f;
a.player.name = "RageCX";
a.player.xp = 125;
a.player.kills = 2;
cout << a;
cout << "* Creating Serializer..." << endl;
Serializer s;
s.SetFile("./char.crl");
cout << "* Writing Character..." << endl;
a.SaveTo(&s);
s.Close();
struct stat fstat;
stat("./char.crl", &fstat);
cout << "Size of file is " << fstat.st_size << endl;
cout << "* Reading back in new object..." << endl;
Serializer s2;
s2.SetFile("./char.crl");
Character b;
b.LoadFrom(&s2);
cout << b;
return 0;
}
@ragecryx
Copy link
Author

ragecryx commented Nov 16, 2016

TODO

  • Add code for handling c-like strings
  • Add code for handling collections

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment