Skip to content

Instantly share code, notes, and snippets.

@MasterAler
Last active February 28, 2022 22:22
Show Gist options
  • Save MasterAler/3aa21d137cbb4c1f1921ba65112c7478 to your computer and use it in GitHub Desktop.
Save MasterAler/3aa21d137cbb4c1f1921ba65112c7478 to your computer and use it in GitHub Desktop.
simple custom serialization example
#pragma once
#include <iostream>
#include <string>
#include <list>
#include <iterator>
#include <type_traits>
#include <algorithm>
/* This is a small & simple example
* of POD types' and STL containers' serialization to a stream
**************************************************************/
namespace custom
{
// --------------------------------- Task 1 -------------------------------------
template<class InputIter, class DiffRes>
void _distance_impl(InputIter first, InputIter last, DiffRes& res, std::random_access_iterator_tag)
{
res = last - first;
}
template<class InputIter, class DiffRes>
void _distance_impl(InputIter first, InputIter last, DiffRes& res, std::bidirectional_iterator_tag)
{
for (; first != last; ++first)
++res;
}
template<class InputIter, class DiffRes>
void _distance_impl(InputIter first, InputIter last, DiffRes& res, std::forward_iterator_tag)
{
for (; first != last; ++first)
++res;
}
template<class InputIter, class DiffRes>
void _distance_impl(InputIter first, InputIter last, DiffRes& res, std::input_iterator_tag)
{
for (; first != last; ++first)
++res;
}
template<class InputIter>
typename std::iterator_traits<InputIter>::difference_type distance(InputIter first, InputIter last)
{
typedef typename std::iterator_traits<InputIter>::iterator_category category;
typename std::iterator_traits<InputIter>::difference_type result = 0;
_distance_impl(first, last, result, category());
return (result);
}
// --------------------------------- Bad magic -------------------------------------
template<typename T, typename _ = void>
struct is_container : std::false_type {};
template<typename... Ts>
struct my_void_t {};
template<typename T>
struct is_container<
T,
std::conditional_t<
false,
my_void_t<
typename T::value_type,
typename T::size_type,
typename T::iterator,
typename T::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>,
void
>
> : public std::true_type {};
//-----
template<typename T, typename _ = void>
struct is_map_container : std::false_type {};
template<typename T>
struct is_map_container<
T,
std::conditional_t<
false,
my_void_t<
typename T::key_type,
typename T::mapped_type,
typename T::value_type,
typename T::key_compare,
typename T::size_type,
typename T::iterator,
typename T::const_iterator,
decltype(std::declval<T>().operator[]( std::declval<T::key_type const&>() )), // operator[] exists
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>,
void
>
> : public std::true_type {};
// ----------------------------------------------------------------------------------
template <typename T>
void _serialize_impl(std::ostream& out, T const& obj, std::false_type, std::false_type)
{
static_assert(std::is_pod<T>::value, "Type is not serializable");
static_assert (!std::is_pointer<typename std::decay<T>::type>::value, "Type is not serializable");
out.write(reinterpret_cast<const char *>(&obj), sizeof(obj));
}
template <typename T>
void _serialize_impl(std::ostream& out, T const& obj, std::true_type, std::false_type)
{
serialize(out, obj.size()); // if we believe it IS stl
// serialize(out, nastya::distance(obj.begin(), obj.end())); // for bicycle-lovers
for(auto it = obj.begin(); it != obj.end(); ++it)
serialize(out, *it);
}
template <typename T>
void _serialize_impl(std::ostream& out, T const& obj, std::true_type, std::true_type)
{
serialize(out, obj.size()); // if we believe it IS stl
for(auto it = obj.begin(); it != obj.end(); ++it)
{
serialize(out, it->first);
serialize(out, it->second);
}
}
template <typename T>
void _deserialize_impl(std::istream& in, T& obj, std::false_type, std::false_type)
{
static_assert(std::is_pod<T>::value, "Type is not serializable");
static_assert (!std::is_pointer<typename std::decay<T>::type>::value, "Type is not serializable");
in.read(reinterpret_cast<char *>(&obj), sizeof(obj));
}
template <typename T>
void _deserialize_impl(std::istream& in, T& obj, std::true_type, std::false_type)
{
typename T::size_type _size;
deserialize(in, _size);
std::list<typename T::value_type> _buffer;
for(typename T::size_type i = 0; i < _size; ++i)
{
typename T::value_type item;
deserialize(in, item);
_buffer.emplace_back(item);
}
std::copy(_buffer.begin(), _buffer.end(), std::back_inserter(obj));
}
template <typename T>
void _deserialize_impl(std::istream& in, T& obj, std::true_type, std::true_type)
{
typename T::size_type _size;
deserialize(in, _size);
for(typename T::size_type i = 0; i < _size; ++i)
{
typename T::key_type _key;
typename T::mapped_type _value;
deserialize(in, _key);
deserialize(in, _value);
obj[_key] = _value;
}
}
// ----------------------------------------------------------------------------------
template <typename T>
void serialize(std::ostream& out, T const& obj)
{
_serialize_impl(out, obj, nastya::is_container<T>::type(), nastya::is_map_container<T>::type());
}
template <typename T>
void deserialize(std::istream& in, T& obj)
{
_deserialize_impl(in, obj, nastya::is_container<T>::type(), nastya::is_map_container<T>::type());
}
}
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <list>
#include <assert.h>
#include <sstream>
#include "serialization.hpp"
using namespace std;
#define TASK_2
#define TASK_3
#define TASK_4
template<class Container>
bool eq_container(Container const& lhs, Container const& rhs) {
if (lhs.size() != rhs.size())
return false;
for (auto it = lhs.begin(), jt = rhs.begin(); it != lhs.end(); ++it, ++jt)
if (!(*it == *jt))
return false;
return true;
}
static void test_pod_serialization() {
#ifdef TASK_2
using namespace std;
using namespace nastya;
std::stringstream stream;
std::ostream& os = stream;
std::istream& is = stream;
struct mypod_t {
size_t a;
size_t b;
unsigned bit:1;
};
mypod_t mypod = {1, 2, 0};
serialize(os, mypod);
mypod_t mypod2;
deserialize(is, mypod2);
assert((mypod.a == mypod2.a)
&& (mypod.b == mypod2.b)
&& (mypod.bit == mypod2.bit));
//-----------------------------------------------
union pw
{
int i;
char ch[2];
};
pw lol;
lol.i = 30024;
serialize(os, lol);
pw lol2;
deserialize(is, lol2);
assert(lol2.ch[0] == 'H');
assert(lol2.ch[1] == 'u');
//-----------------------------------------------
std::string govn("Go fuck yourself");
serialize(os, govn);
std::string other;
deserialize(is, other);
assert(govn == other);
// int govn00[] = {1, 33, 566666, 77};
// serialize(os, govn00);
// int* shit;
// deserialize(is, shit);
// for(size_t i = 0; i < sizeof(shit) / sizeof(0[shit]); ++i)
// {
// cout << shit[i];
// }
#endif
}
static void test_pod_vector_serialize() {
#ifdef TASK_3
using namespace std;
using namespace nastya;
std::stringstream stream;
std::ostream& os = stream;
std::istream& is = stream;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
serialize(os, v);
vector<int> v2;
deserialize(is, v2);
assert(eq_container(v, v2));
#endif
}
static void test_nonpod_vector_serialize() {
#ifdef TASK_3
using namespace std;
using namespace nastya;
std::stringstream stream;
std::ostream& os = stream;
std::istream& is = stream;
std::vector<string> v;
v.push_back("1");
v.push_back("2");
v.push_back("3");
v.push_back("4");
serialize(os, v);
vector<string> v2;
deserialize(is, v2);
assert(eq_container(v, v2));
#endif
}
static void test_nonpod_map_serialize() {
#ifdef TASK_4
using namespace std;
using namespace nastya;
std::stringstream stream;
std::ostream& os = stream;
std::istream& is = stream;
std::unordered_map<string, int> v;
v["1"] = 1;
v["2"] = 2;
v["3"] = 3;
v["4"] = 4;
serialize(os, v);
unordered_map<string, int> v2;
deserialize(is, v2);
assert(eq_container(v, v2));
#endif
}
struct custom_record {
custom_record()
: number(0) {}
custom_record(std::string const& t, int n)
: text(t), number(n), texts(n, t) {}
friend void serialize(std::ostream& s, custom_record const& r) {
nastya::serialize(s, r.text);
nastya::serialize(s, r.number);
nastya::serialize(s, r.texts);
}
friend void deserialize(std::istream& s, custom_record& r) {
nastya::deserialize(s, r.text);
nastya::deserialize(s, r.number);
nastya::deserialize(s, r.texts);
}
friend bool operator==(custom_record const& lhs,
custom_record const& rhs) {
return lhs.text == rhs.text
&& lhs.number == rhs.number
&& eq_container(lhs.texts, rhs.texts);
}
private:
std::string text;
int number;
std::vector<std::string> texts;
};
static void test_custom_struct_serialize() {
#ifdef TASK_4
using namespace std;
using namespace nastya;
std::stringstream stream;
std::ostream& os = stream;
std::istream& is = stream;
list<custom_record> v;
v.push_back(custom_record("1", 1));
v.push_back(custom_record("2", 2));
v.push_back(custom_record("3", 3));
v.push_back(custom_record("4", 4));
serialize(os, v);
list<custom_record> v2;
deserialize(is, v2);
assert(eq_container(v, v2));
#endif
}
/*******************************************************************************/
int main()
{
// Task 2
test_pod_serialization();
// Task 3
test_pod_vector_serialize();
// Task 3
test_nonpod_vector_serialize();
// Task 4
test_nonpod_map_serialize();
// Task 4
test_custom_struct_serialize();
std::cout << "OK" << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment