Last active
February 28, 2022 22:22
-
-
Save MasterAler/3aa21d137cbb4c1f1921ba65112c7478 to your computer and use it in GitHub Desktop.
simple custom serialization example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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