Reputation: 67
So, I´d want to implement simple serialization for some int variables in C++ and I really don´t know how...
My goal is the following:
I essentially want to be able to convert any integer to binary, preferably with a simple function call.
// Here´s some dummy code of what I essentially want to do
int TestVariable = 25;
String FilePath = "D:\dev\Test.txt";
Serialize(TestVariable, FilePath);
// [...]
// at some later point in the code, when I want to access the file
Deserialize(&TestVariable, FilePath);
I already heard of libraries like Boost, but I think that´d be a bit overkill when I just want to serialize simple variables.
Already thank you in advance for your answers. :D
Upvotes: 0
Views: 1881
Reputation: 91
First of all, there is a little "inconsistency": you're asking for binary serialization, in something that looks like a text file. I will assume you really want a binary output.
The only thing to take care about when serializing integers is the endianness of the machine (even though most of machines are little endian).
In C++17 or lower the easiest way is a runtime check like
inline bool littleEndian()
{
static const uint32_t test = 0x01020304;
return *((uint8_t *)&test) == 0x04;
}
C++20 introduces a compile-time check so you can rewrite the previous as
constexpr bool littleEndian()
{
return std::endian::native == std::endian::little;
}
At this point what you want, is writing in a standard way all integers. Usually BigEndian is the standard.
template <typename T>
inline static T revert(T num)
{
T res;
for (std::size_t i = 0; i < sizeof(T); i++)
((uint8_t *)&res)[i] = ((uint8_t *)&num)[sizeof(T) - 1 - i];
return res;
}
at this point your serializer would be:
template <typename T>
void serialize(T TestVariable, std::string& FilePath)
{
static_assert(std::is_integral<T>::value); //check that T is of {char, int, ...} type
static_assert(!std::is_reference<T>::value); //check that T is not a reference
std::ofstream o(FilePath);
if (littleEndian())
TestVariable = revert(TestVariable);
o.write((char *)&TestVariable, sizeof(T));
}
and your deserializer would be
template <typename T>
void deserialize(T *TestVariable, std::string FilePath)
{
static_assert(std::is_integral<T>::value);
std::ifstream i(FilePath);
i.read((char *)TestVariable, sizeof(T));
if (littleEndian())
*TestVariable = revert(*TestVariable);
}
Notice: this code is just an example that works with your interface, you just have to include <iostream>
, <fstream>
and if you're using the c++20 version, include <bit>
Upvotes: 2
Reputation: 3968
First let me laydown the reasons not to do this:
But if you really what to do something custom made you can simply use streams, here is an example using stringstream (I always use stringstream in my unit test because I want them to be quick), but you can simply modify it to use filestream.
Please note, the type must be default constructable to be used by deserialize template function. That must be a very stringent requirement for complex classes.
#include <sstream>
#include <iostream>
template<typename T>
void serialize(std::ostream& os, const T& value)
{
os << value;
}
template<typename T>
T deserialize(std::istream& is)
{
T value;
is >> value;
return value;
}
int main()
{
std::stringstream ss;
serialize(ss, 1353);
serialize(ss, std::string("foobar"));
std::cout << deserialize<int>(ss) << " " << deserialize<std::string>(ss) << std::endl;
return 0;
}
Upvotes: 1