Commit 010f3f6b authored by neverlord's avatar neverlord

serialization improvements

parent 123e61f7
......@@ -2,10 +2,12 @@
#define TO_UNIFORM_NAME_HPP
#include <string>
#include <typeinfo>
namespace cppa { namespace detail {
std::string to_uniform_name(const std::string& demangled_name);
std::string to_uniform_name(const std::type_info& tinfo);
} } // namespace cppa::detail
......
......@@ -14,177 +14,177 @@ namespace {
constexpr const char* mapped_int_names[][2] =
{
{ nullptr, nullptr }, // sizeof 0 { invalid }
{ "@i8", "@u8" }, // sizeof 1 -> signed / unsigned int8
{ "@i16", "@u16" }, // sizeof 2 -> signed / unsigned int16
{ nullptr, nullptr }, // sizeof 3 { invalid }
{ "@i32", "@u32" }, // sizeof 4 -> signed / unsigned int32
{ nullptr, nullptr }, // sizeof 5 { invalid }
{ nullptr, nullptr }, // sizeof 6 { invalid }
{ nullptr, nullptr }, // sizeof 7 { invalid }
{ "@i64", "@u64" } // sizeof 8 -> signed / unsigned int64
{ nullptr, nullptr }, // sizeof 0 { invalid }
{ "@i8", "@u8" }, // sizeof 1 -> signed / unsigned int8
{ "@i16", "@u16" }, // sizeof 2 -> signed / unsigned int16
{ nullptr, nullptr }, // sizeof 3 { invalid }
{ "@i32", "@u32" }, // sizeof 4 -> signed / unsigned int32
{ nullptr, nullptr }, // sizeof 5 { invalid }
{ nullptr, nullptr }, // sizeof 6 { invalid }
{ nullptr, nullptr }, // sizeof 7 { invalid }
{ "@i64", "@u64" } // sizeof 8 -> signed / unsigned int64
};
template<typename T>
constexpr size_t sign_index()
{
static_assert(std::numeric_limits<T>::is_integer, "T is not an integer");
return std::numeric_limits<T>::is_signed ? 0 : 1;
static_assert(std::numeric_limits<T>::is_integer, "T is not an integer");
return std::numeric_limits<T>::is_signed ? 0 : 1;
}
template<typename T>
inline std::string demangled()
{
return cppa::detail::demangle(typeid(T).name());
return cppa::detail::demangle(typeid(T).name());
}
template<typename T>
constexpr const char* mapped_int_name()
{
return mapped_int_names[sizeof(T)][sign_index<T>()];
return mapped_int_names[sizeof(T)][sign_index<T>()];
}
template<typename Iterator>
std::string to_uniform_name_impl(Iterator begin, Iterator end,
bool first_run = false)
bool first_run = false)
{
// all integer type names as uniform representation
static std::map<std::string, std::string> mapped_demangled_names =
{
{ demangled<char>(), mapped_int_name<char>() },
{ demangled<signed char>(), mapped_int_name<signed char>() },
{ demangled<unsigned char>(), mapped_int_name<unsigned char>() },
{ demangled<short>(), mapped_int_name<short>() },
{ demangled<signed short>(), mapped_int_name<signed short>() },
{ demangled<unsigned short>(), mapped_int_name<unsigned short>() },
{ demangled<short int>(), mapped_int_name<short int>() },
{ demangled<signed short int>(), mapped_int_name<signed short int>() },
{ demangled<unsigned short int>(), mapped_int_name<unsigned short int>()},
{ demangled<int>(), mapped_int_name<int>() },
{ demangled<signed int>(), mapped_int_name<signed int>() },
{ demangled<unsigned int>(), mapped_int_name<unsigned int>() },
{ demangled<long int>(), mapped_int_name<long int>() },
{ demangled<signed long int>(), mapped_int_name<signed long int>() },
{ demangled<unsigned long int>(), mapped_int_name<unsigned long int>() },
{ demangled<long>(), mapped_int_name<long>() },
{ demangled<signed long>(), mapped_int_name<signed long>() },
{ demangled<unsigned long>(), mapped_int_name<unsigned long>() },
{ demangled<long long>(), mapped_int_name<long long>() },
{ demangled<signed long long>(), mapped_int_name<signed long long>() },
{ demangled<unsigned long long>(), mapped_int_name<unsigned long long>()},
{ demangled<wchar_t>(), mapped_int_name<wchar_t>() },
{ demangled<char16_t>(), mapped_int_name<char16_t>() },
{ demangled<char32_t>(), mapped_int_name<char32_t>() },
{ demangled<cppa::util::void_type>(), "@0" },
{ demangled<std::wstring>(), "@wstr" },
{ demangled<std::string>(), "@str" }
};
// check if we could find the whole string in our lookup map
if (first_run)
{
std::string tmp(begin, end);
auto i = mapped_demangled_names.find(tmp);
if (i != mapped_demangled_names.end())
{
return i->second;
}
}
// does [begin, end) represents an empty string?
if (begin == end) return "";
// derived reverse_iterator type
typedef std::reverse_iterator<Iterator> reverse_iterator;
// a subsequence [begin', end') within [begin, end)
typedef std::pair<Iterator, Iterator> subseq;
std::vector<subseq> substrings;
// explode string if we got a list of types
int open_brackets = 0; // counts "open" '<'
// denotes the begin of a possible subsequence
Iterator anchor = begin;
for (Iterator i = begin; i != end; /* i is incemented in the loop */)
{
switch (*i)
{
case '<':
++open_brackets;
++i;
break;
case '>':
if (--open_brackets < 0)
{
throw std::runtime_error("malformed string");
}
++i;
break;
case ',':
if (open_brackets == 0)
{
substrings.push_back(std::make_pair(anchor, i));
++i;
anchor = i;
}
else
{
++i;
}
break;
default:
++i;
break;
}
}
// call recursively for each list argument
if (!substrings.empty())
{
std::string result;
substrings.push_back(std::make_pair(anchor, end));
for (const subseq& sstr : substrings)
{
if (!result.empty()) result += ",";
result += to_uniform_name_impl(sstr.first, sstr.second);
}
return result;
}
// we didn't got a list, compute unify name
else
{
// is [begin, end) a template?
Iterator substr_begin = std::find(begin, end, '<');
if (substr_begin == end)
{
// not a template, return mapping
std::string arg(begin, end);
auto mapped = mapped_demangled_names.find(arg);
return (mapped == mapped_demangled_names.end()) ? arg : mapped->second;
}
// skip leading '<'
++substr_begin;
// find trailing '>'
Iterator substr_end = std::find(reverse_iterator(end),
reverse_iterator(substr_begin),
'>')
// get as an Iterator
.base();
// skip trailing '>'
--substr_end;
if (substr_end == substr_begin)
{
throw std::runtime_error("substr_end == substr_begin");
}
std::string result;
// template name (part before leading '<')
result.append(begin, substr_begin);
// get mappings of all template parameter(s)
result += to_uniform_name_impl(substr_begin, substr_end);
result.append(substr_end, end);
return result;
}
// all integer type names as uniform representation
static std::map<std::string, std::string> mapped_demangled_names =
{
{ demangled<char>(), mapped_int_name<char>() },
{ demangled<signed char>(), mapped_int_name<signed char>() },
{ demangled<unsigned char>(), mapped_int_name<unsigned char>() },
{ demangled<short>(), mapped_int_name<short>() },
{ demangled<signed short>(), mapped_int_name<signed short>() },
{ demangled<unsigned short>(), mapped_int_name<unsigned short>() },
{ demangled<short int>(), mapped_int_name<short int>() },
{ demangled<signed short int>(), mapped_int_name<signed short int>() },
{ demangled<unsigned short int>(), mapped_int_name<unsigned short int>()},
{ demangled<int>(), mapped_int_name<int>() },
{ demangled<signed int>(), mapped_int_name<signed int>() },
{ demangled<unsigned int>(), mapped_int_name<unsigned int>() },
{ demangled<long int>(), mapped_int_name<long int>() },
{ demangled<signed long int>(), mapped_int_name<signed long int>() },
{ demangled<unsigned long int>(), mapped_int_name<unsigned long int>() },
{ demangled<long>(), mapped_int_name<long>() },
{ demangled<signed long>(), mapped_int_name<signed long>() },
{ demangled<unsigned long>(), mapped_int_name<unsigned long>() },
{ demangled<long long>(), mapped_int_name<long long>() },
{ demangled<signed long long>(), mapped_int_name<signed long long>() },
{ demangled<unsigned long long>(), mapped_int_name<unsigned long long>()},
{ demangled<wchar_t>(), mapped_int_name<wchar_t>() },
{ demangled<char16_t>(), mapped_int_name<char16_t>() },
{ demangled<char32_t>(), mapped_int_name<char32_t>() },
{ demangled<cppa::util::void_type>(), "@0" },
{ demangled<std::wstring>(), "@wstr" },
{ demangled<std::string>(), "@str" }
};
// check if we could find the whole string in our lookup map
if (first_run)
{
std::string tmp(begin, end);
auto i = mapped_demangled_names.find(tmp);
if (i != mapped_demangled_names.end())
{
return i->second;
}
}
// does [begin, end) represents an empty string?
if (begin == end) return "";
// derived reverse_iterator type
typedef std::reverse_iterator<Iterator> reverse_iterator;
// a subsequence [begin', end') within [begin, end)
typedef std::pair<Iterator, Iterator> subseq;
std::vector<subseq> substrings;
// explode string if we got a list of types
int open_brackets = 0; // counts "open" '<'
// denotes the begin of a possible subsequence
Iterator anchor = begin;
for (Iterator i = begin; i != end; /* i is incemented in the loop */)
{
switch (*i)
{
case '<':
++open_brackets;
++i;
break;
case '>':
if (--open_brackets < 0)
{
throw std::runtime_error("malformed string");
}
++i;
break;
case ',':
if (open_brackets == 0)
{
substrings.push_back(std::make_pair(anchor, i));
++i;
anchor = i;
}
else
{
++i;
}
break;
default:
++i;
break;
}
}
// call recursively for each list argument
if (!substrings.empty())
{
std::string result;
substrings.push_back(std::make_pair(anchor, end));
for (const subseq& sstr : substrings)
{
if (!result.empty()) result += ",";
result += to_uniform_name_impl(sstr.first, sstr.second);
}
return result;
}
// we didn't got a list, compute unify name
else
{
// is [begin, end) a template?
Iterator substr_begin = std::find(begin, end, '<');
if (substr_begin == end)
{
// not a template, return mapping
std::string arg(begin, end);
auto mapped = mapped_demangled_names.find(arg);
return (mapped == mapped_demangled_names.end()) ? arg : mapped->second;
}
// skip leading '<'
++substr_begin;
// find trailing '>'
Iterator substr_end = std::find(reverse_iterator(end),
reverse_iterator(substr_begin),
'>')
// get as an Iterator
.base();
// skip trailing '>'
--substr_end;
if (substr_end == substr_begin)
{
throw std::runtime_error("substr_end == substr_begin");
}
std::string result;
// template name (part before leading '<')
result.append(begin, substr_begin);
// get mappings of all template parameter(s)
result += to_uniform_name_impl(substr_begin, substr_end);
result.append(substr_end, end);
return result;
}
}
} // namespace <anonymous>
......@@ -193,22 +193,28 @@ namespace cppa { namespace detail {
std::string to_uniform_name(const std::string& dname)
{
static std::string an = "(anonymous namespace)";
static std::string an_replacement = "@_";
auto r = to_uniform_name_impl(dname.begin(), dname.end(), true);
// replace all occurrences of an with "@_"
if (r.size() > an.size())
{
auto i = std::search(r.begin(), r.end(), an.begin(), an.end());
while (i != r.end())
{
auto substr_end = i + an.size();
r.replace(i, substr_end, an_replacement);
// next iteration
i = std::search(r.begin(), r.end(), an.begin(), an.end());
}
}
return r;
static std::string an = "(anonymous namespace)";
static std::string an_replacement = "@_";
auto r = to_uniform_name_impl(dname.begin(), dname.end(), true);
// replace all occurrences of an with "@_"
if (r.size() > an.size())
{
auto i = std::search(r.begin(), r.end(), an.begin(), an.end());
while (i != r.end())
{
auto substr_end = i + an.size();
r.replace(i, substr_end, an_replacement);
// next iteration
i = std::search(r.begin(), r.end(), an.begin(), an.end());
}
}
return r;
}
std::string to_uniform_name(const std::type_info& tinfo)
{
return to_uniform_name(demangle(tinfo.name()));
}
} } // namespace cppa::detail
#include <new>
#include <set>
#include <locale>
#include <memory>
#include <string>
......@@ -162,8 +163,6 @@ struct type_to_ptype_impl
template<typename T>
struct type_to_ptype : type_to_ptype_impl<typename plain<T>::type> { };
namespace trait {
template<typename T>
struct is_primitive
{
......@@ -191,12 +190,58 @@ class is_iterable
public:
static const bool value = !is_primitive<T>::value
static const bool value = is_primitive<T>::value == false
&& std::is_same<bool, result_type>::value;
};
} // namespace trait
template<typename T>
class has_push_back
{
template<class C>
static bool cmp_help_fun(C* arg0,
decltype(arg0->push_back(typename C::value_type()))*)
{
return true;
}
static void cmp_help_fun(void*, void*) { }
typedef decltype(cmp_help_fun(static_cast<T*>(nullptr),
static_cast<void*>(nullptr)))
result_type;
public:
static const bool value = is_iterable<T>::value
&& std::is_same<bool, result_type>::value;
};
template<typename T>
class has_insert
{
template<class C>
static bool cmp_help_fun(C* arg0,
decltype((arg0->insert(typename C::value_type())).second)*)
{
return true;
}
static void cmp_help_fun(void*, void*) { }
typedef decltype(cmp_help_fun(static_cast<T*>(nullptr),
static_cast<bool*>(nullptr)))
result_type;
public:
static const bool value = is_iterable<T>::value
&& std::is_same<bool, result_type>::value;
};
class pt_value;
......@@ -282,7 +327,7 @@ class pt_value
template<primitive_type FT, class T, class V>
static void set(primitive_type& lhs_type, T& lhs, V&& rhs,
typename disable_if<std::is_arithmetic<T>>::type* = 0)
typename disable_if< std::is_arithmetic<T> >::type* = 0)
{
if (FT == lhs_type)
{
......@@ -297,7 +342,7 @@ class pt_value
template<primitive_type FT, class T, class V>
static void set(primitive_type& lhs_type, T& lhs, V&& rhs,
typename enable_if<std::is_arithmetic<T>>::type* = 0)
typename enable_if<std::is_arithmetic<T>, int>::type* = 0)
{
// don't call a constructor for arithmetic types
lhs = rhs;
......@@ -305,17 +350,17 @@ class pt_value
}
template<class T>
static void destroy(T& what,
typename disable_if<std::is_arithmetic<T>>::type* = 0)
inline static void destroy(T&,
typename enable_if<std::is_arithmetic<T>, int>::type* = 0)
{
what.~T();
// arithmetic types don't need destruction
}
template<class T>
static void destroy(T&,
typename enable_if<std::is_arithmetic<T>>::type* = 0)
inline static void destroy(T& what,
typename disable_if< std::is_arithmetic<T> >::type* = 0)
{
// arithmetic types don't need destruction
what.~T();
}
struct destroyer
......@@ -447,7 +492,7 @@ class pt_value
pt_value(V&& value) : m_ptype(pt_null)
{
static_assert(type_to_ptype<V>::ptype != pt_null,
"T couldn't be mapped to an ptype");
"V is not a primitive type");
set<type_to_ptype<V>::ptype>(m_ptype,
get(pt_token<type_to_ptype<V>::ptype>()),
std::forward<V>(value));
......@@ -510,30 +555,30 @@ class pt_value
};
template<typename T>
typename enable_if<trait::is_primitive<T>, bool>::type
typename enable_if<is_primitive<T>, bool>::type
operator==(const T& lhs, const pt_value& rhs)
{
constexpr primitive_type ptype = type_to_ptype<T>::ptype;
static constexpr primitive_type ptype = type_to_ptype<T>::ptype;
static_assert(ptype != pt_null, "T couldn't be mapped to an ptype");
return (rhs.ptype() == ptype) ? lhs == pt_value_cast<ptype>(rhs) : false;
}
template<typename T>
typename enable_if<trait::is_primitive<T>, bool>::type
typename enable_if<is_primitive<T>, bool>::type
operator==(const pt_value& lhs, const T& rhs)
{
return (rhs == lhs);
}
template<typename T>
typename enable_if<trait::is_primitive<T>, bool>::type
typename enable_if<is_primitive<T>, bool>::type
operator!=(const pt_value& lhs, const T& rhs)
{
return !(lhs == rhs);
}
template<typename T>
typename enable_if<trait::is_primitive<T>, bool>::type
typename enable_if<is_primitive<T>, bool>::type
operator!=(const T& lhs, const pt_value& rhs)
{
return !(lhs == rhs);
......@@ -613,74 +658,96 @@ class serializer
virtual void begin_object(const std::string& type_name) = 0;
virtual void end_object() = 0;
virtual void begin_list(size_t size) = 0;
virtual void end_list() = 0;
virtual void begin_sequence(size_t size) = 0;
virtual void end_sequence() = 0;
/**
* @brief Writes a single value.
*/
virtual void write_value(const pt_value& value) = 0;
};
class serializer;
class deserializer;
class meta_type
{
public:
virtual ~meta_type() { }
/**
* @brief Create an instance of this type, initialized
* with its default constructor.
* @brief Writes @p size values.
*/
virtual void* default_constructed() = 0;
virtual void delete_instance(void*) = 0;
virtual void serialize(void*, serializer*) = 0;
virtual void deserialize(void*, deserializer*) = 0;
virtual void write_tuple(size_t size, const pt_value* values) = 0;
};
std::map<std::string, meta_type*> s_meta_types;
/**
*
*/
class deserializer
{
public:
/**
* @brief Seek the beginning of the next object and return its type name.
* @brief Seeks the beginning of the next object and return its type name.
*/
virtual std::string seek_object() = 0;
/**
* @brief Seek the beginning of the next object and return its type name,
* but don't modify the internal in-stream position.
* @brief Equal to {@link seek_object()} but doesn't
* modify the internal in-stream position.
*/
virtual std::string peek_object() = 0;
virtual void begin_object(const std::string& type_name) = 0;
virtual void end_object() = 0;
virtual size_t begin_list(primitive_type value_type) = 0;
virtual void end_list() = 0;
virtual size_t begin_sequence() = 0;
virtual void end_sequence() = 0;
virtual pt_value read_value(primitive_type ptype) = 0;
virtual void read_tuple(size_t size,
const primitive_type* ptypes,
pt_value* storage ) = 0;
};
class meta_type
{
public:
virtual ~meta_type() { }
/**
* @brief Creates an instance of this type, initialized
* with the default constructor.
*/
virtual void* new_instance() const = 0;
/**
* @brief Cast @p instance to the native type and delete it.
*/
virtual void delete_instance(void* instance) const = 0;
/**
* @brief Serializes @p instance to @p sink.
*/
virtual void serialize(const void* instance, serializer* sink) const = 0;
/**
* @brief Deserializes @p instance from @p source.
*/
virtual void deserialize(void* instance, deserializer* source) const = 0;
};
std::map<std::string, meta_type*> s_meta_types;
class root_object
{
public:
std::pair<void*, meta_type*> deserialize(deserializer* d)
/**
* @brief Deserializes a new object from @p source and returns the
* new (deserialized) instance with its meta_type.
*/
std::pair<void*, meta_type*> deserialize(deserializer* source) const
{
void* result;
std::string tname = d->peek_object();
std::string tname = source->peek_object();
auto i = s_meta_types.find(tname);
if (i == s_meta_types.end())
{
......@@ -691,14 +758,14 @@ class root_object
{
throw std::logic_error("mobj == nullptr");
}
result = mobj->default_constructed();
result = mobj->new_instance();
if (result == nullptr)
{
throw std::logic_error("result == nullptr");
}
try
{
mobj->deserialize(result, d);
mobj->deserialize(result, source);
}
catch (...)
{
......@@ -710,8 +777,11 @@ class root_object
};
/**
* @brief {@link meta_type} implementation for primitive data types.
*/
template<typename T>
class meta_value_property : public meta_type
class primitive_member : public meta_type
{
static constexpr primitive_type ptype = type_to_ptype<T>::ptype;
......@@ -720,127 +790,324 @@ class meta_value_property : public meta_type
public:
meta_value_property() { }
pt_value get(void* obj)
{
return *reinterpret_cast<T*>(obj);
}
void set(void* obj, pt_value&& value)
{
*reinterpret_cast<T*>(obj) = std::move(pt_value_cast<T&>(value));
}
void* default_constructed()
void* new_instance() const
{
return new T();
}
void delete_instance(void* ptr)
void delete_instance(void* ptr) const
{
delete reinterpret_cast<T*>(ptr);
}
void serialize(void* obj, serializer* s)
void serialize(const void* obj, serializer* s) const
{
s->write_value(get(obj));
s->write_value(*reinterpret_cast<const T*>(obj));
}
void deserialize(void* obj, deserializer* d)
void deserialize(void* obj, deserializer* d) const
{
set(obj, d->read_value(ptype));
pt_value val(d->read_value(ptype));
*reinterpret_cast<T*>(obj) = std::move(pt_value_cast<T&>(val));
}
};
// std::vector or std::list
/**
* @brief {@link meta_type} implementation for STL compliant
* lists (such as std::vector and std::list).
*
* This implementation requires a primitive List::value_type.
*/
template<typename List>
class meta_list_property : public meta_type
class list_member : public meta_type
{
typedef typename List::value_type value_type;
static constexpr primitive_type vptype = type_to_ptype<value_type>::ptype;
static_assert(vptype != pt_null, "T doesn't have a primitive value_type");
static_assert(vptype != pt_null,
"List::value_type is not a primitive data type");
public:
meta_list_property() { }
void serialize(void* obj, serializer* s)
void serialize(const void* obj, serializer* s) const
{
auto& ls = *reinterpret_cast<List*>(obj);
s->begin_list(ls.size());
auto& ls = *reinterpret_cast<const List*>(obj);
s->begin_sequence(ls.size());
for (const auto& val : ls)
{
s->write_value(val);
}
s->end_list();
s->end_sequence();
}
void deserialize(void* obj, deserializer* d)
void deserialize(void* obj, deserializer* d) const
{
auto& ls = *reinterpret_cast<List*>(obj);
size_t ls_size = d->begin_list(vptype);
size_t ls_size = d->begin_sequence();
for (size_t i = 0; i < ls_size; ++i)
{
pt_value val = d->read_value(vptype);
ls.push_back(std::move(pt_value_cast<value_type&>(val)));
}
d->end_list();
d->end_sequence();
}
void* default_constructed()
void* new_instance() const
{
return new List();
}
void delete_instance(void* ptr)
void delete_instance(void* ptr) const
{
delete reinterpret_cast<List*>(ptr);
}
};
template<class Object>
class meta_object : public meta_type
/**
* @brief {@link meta_type} implementation for std::pair.
*/
template<typename T1, typename T2>
class pair_member : public meta_type
{
static constexpr primitive_type ptype1 = type_to_ptype<T1>::ptype;
static constexpr primitive_type ptype2 = type_to_ptype<T2>::ptype;
static_assert(ptype1 != pt_null, "T1 is not a primitive type");
static_assert(ptype2 != pt_null, "T2 is not a primitive type");
typedef std::pair<T1, T2> pair_type;
public:
void serialize(const void* obj, serializer* s) const
{
auto& p = *reinterpret_cast<const pair_type*>(obj);
pt_value values[2] = { p.first, p.second };
s->write_tuple(2, values);
}
void deserialize(void* obj, deserializer* d) const
{
primitive_type ptypes[2] = { ptype1, ptype2 };
pt_value values[2];
d->read_tuple(2, ptypes, values);
auto& p = *reinterpret_cast<pair_type*>(obj);
p.first = std::move(pt_value_cast<T1&>(values[0]));
p.second = std::move(pt_value_cast<T2&>(values[1]));
}
void* new_instance() const
{
return new pair_type();
}
void delete_instance(void* ptr) const
{
delete reinterpret_cast<pair_type*>(ptr);
}
};
// matches value_type of std::set
template<typename T>
struct meta_value_type
{
primitive_member<T> impl;
void serialize_value(const T& what, serializer* s) const
{
impl.serialize(&what, s);
}
template<typename M>
void deserialize_and_insert(M& map, deserializer* d) const
{
T value;
impl.deserialize(&value, d);
map.insert(std::move(value));
}
};
// matches value_type of std::map
template<typename T1, typename T2>
struct meta_value_type<std::pair<const T1, T2>>
{
pair_member<T1, T2> impl;
void serialize_value(const std::pair<const T1, T2>& what, serializer* s) const
{
std::pair<T1, T2> p(what.first, what.second);
impl.serialize(&p, s);
}
template<typename M>
void deserialize_and_insert(M& map, deserializer* d) const
{
std::pair<T1, T2> p;
impl.deserialize(&p, d);
std::pair<const T1, T2> v(std::move(p.first), std::move(p.second));
map.insert(std::move(v));
}
};
/**
* @brief {@link meta_type} implementation for STL compliant
* maps (such as std::map and std::set).
*
* This implementation requires primitive key and value types
* (or a pair of primitive types as value type).
*/
template<typename Map>
class map_member : public meta_type
{
typedef typename Map::key_type key_type;
typedef typename Map::value_type value_type;
meta_value_type<value_type> m_value_type_meta;
public:
void serialize(const void* obj, serializer* s) const
{
auto& mp = *reinterpret_cast<const Map*>(obj);
s->begin_sequence(mp.size());
for (const auto& val : mp)
{
m_value_type_meta.serialize_value(val, s);
}
s->end_sequence();
}
void deserialize(void* obj, deserializer* d) const
{
auto& mp = *reinterpret_cast<Map*>(obj);
size_t mp_size = d->begin_sequence();
for (size_t i = 0; i < mp_size; ++i)
{
m_value_type_meta.deserialize_and_insert(mp, d);
}
d->end_sequence();
}
void* new_instance() const
{
return new Map();
}
void delete_instance(void* ptr) const
{
delete reinterpret_cast<Map*>(ptr);
}
};
/**
* @brief {@link meta_type} implementation for user-defined structs.
*/
template<class Struct>
class meta_struct : public meta_type
{
template<typename T>
struct is_invalid
{
static const bool value = !is_primitive<T>::value
&& !has_push_back<T>::value
&& !has_insert<T>::value;
};
class member
{
meta_type* m_meta;
std::function<void* (void*)> m_deref;
std::function<void (const meta_type*,
const void*,
serializer* )> m_serialize;
std::function<void (const meta_type*,
void*,
deserializer* )> m_deserialize;
member(const member&) = delete;
member& operator=(const member&) = delete;
void swap(member& other)
{
std::swap(m_meta, other.m_meta);
std::swap(m_serialize, other.m_serialize);
std::swap(m_deserialize, other.m_deserialize);
}
template<typename S, class D>
member(meta_type* mtptr, S&& s, D&& d)
: m_meta(mtptr)
, m_serialize(std::forward<S>(s))
, m_deserialize(std::forward<D>(d))
{
}
public:
template<typename T, class C>
member(meta_type* mtptr, T C::*mem_ptr) : m_meta(mtptr)
{
m_deref = [mem_ptr] (void* obj) -> void*
m_serialize = [mem_ptr] (const meta_type* mt,
const void* obj,
serializer* s)
{
mt->serialize(&(*reinterpret_cast<const C*>(obj).*mem_ptr), s);
};
m_deserialize = [mem_ptr] (const meta_type* mt,
void* obj,
deserializer* d)
{
return &(*reinterpret_cast<C*>(obj).*mem_ptr);
mt->deserialize(&(*reinterpret_cast<C*>(obj).*mem_ptr), d);
};
}
member(meta_type* pptr, std::function<void* (void*)>&& gpm)
: m_meta(pptr), m_deref(std::move(gpm))
member(member&& other) : m_meta(nullptr)
{
swap(other);
}
// a member that's not a member at all, but "forwards"
// the 'self' pointer to make use *_member implementations
static member fake_member(meta_type* mtptr)
{
return {
mtptr,
[] (const meta_type* mt, const void* obj, serializer* s)
{
mt->serialize(obj, s);
},
[] (const meta_type* mt, void* obj, deserializer* d)
{
mt->deserialize(obj, d);
}
};
}
member(member&&) = default;
~member()
{
delete m_meta;
}
member(const member&) = default;
member& operator=(member&& other)
{
swap(other);
return *this;
}
inline void serialize(void* parent, serializer* s)
inline void serialize(const void* parent, serializer* s) const
{
m_meta->serialize(m_deref(parent), s);
m_serialize(m_meta, parent, s);
}
inline void deserialize(void* parent, deserializer* s)
inline void deserialize(void* parent, deserializer* d) const
{
m_meta->deserialize(m_deref(parent), s);
m_deserialize(m_meta, parent, d);
}
};
......@@ -851,47 +1118,75 @@ class meta_object : public meta_type
// terminates recursion
inline void push_back() { }
// pr.first = member pointer
// pr.second = meta object to handle pr.first
template<typename T, class C, typename... Args>
void push_back(std::pair<T C::*, meta_object<T>*> pr, const Args&... args)
void push_back(std::pair<T C::*, meta_struct<T>*> pr, const Args&... args)
{
m_members.push_back({ pr.second, pr.first });
push_back(args...);
}
template<class C, typename T, typename... Args>
typename enable_if<trait::is_primitive<T> >::type
typename enable_if<is_primitive<T> >::type
push_back(T C::*mem_ptr, const Args&... args)
{
m_members.push_back({ new meta_value_property<T>(), mem_ptr });
m_members.push_back({ new primitive_member<T>(), mem_ptr });
push_back(args...);
}
template<class C, typename T, typename... Args>
typename enable_if<trait::is_iterable<T> >::type
typename enable_if<has_push_back<T> >::type
push_back(T C::*mem_ptr, const Args&... args)
{
m_members.push_back({ new meta_list_property<T>(), mem_ptr });
m_members.push_back({ new list_member<T>(), mem_ptr });
push_back(args...);
}
template<class C, typename T, typename... Args>
typename disable_if_c< trait::is_primitive<T>::value
|| trait::is_iterable<T>::value, void>::type
typename enable_if<has_insert<T> >::type
push_back(T C::*mem_ptr, const Args&... args)
{
static_assert(trait::is_primitive<T>::value,
"T is neither a primitive type nor an iterable type");
m_members.push_back({ new map_member<T>(), mem_ptr });
push_back(args...);
}
template<class C, typename T, typename... Args>
typename enable_if<is_invalid<T>>::type
push_back(T C::*mem_ptr, const Args&... args)
{
static_assert(is_primitive<T>::value,
"T is neither a primitive type nor a list nor a map");
}
template<typename T>
void init_(typename enable_if<is_primitive<T>>::type* = nullptr)
{
m_members.push_back(member::fake_member(new primitive_member<T>()));
}
template<typename T>
void init_(typename disable_if<is_primitive<T>>::type* = nullptr)
{
static_assert(is_primitive<T>::value,
"T is neither a primitive type nor a list nor a map");
}
public:
template<typename... Args>
meta_object(const Args&... args) : class_name(cppa::detail::to_uniform_name(cppa::detail::demangle(typeid(Object).name())))
meta_struct(const Args&... args)
: class_name(cppa::detail::to_uniform_name(typeid(Struct)))
{
push_back(args...);
}
void serialize(void* obj, serializer* s)
meta_struct() : class_name(cppa::detail::to_uniform_name(typeid(Struct)))
{
init_<Struct>();
}
void serialize(const void* obj, serializer* s) const
{
s->begin_object(class_name);
for (auto& m : m_members)
......@@ -901,7 +1196,7 @@ class meta_object : public meta_type
s->end_object();
}
void deserialize(void* obj, deserializer* d)
void deserialize(void* obj, deserializer* d) const
{
std::string cname = d->seek_object();
if (cname != class_name)
......@@ -916,14 +1211,14 @@ class meta_object : public meta_type
d->end_object();
}
void* default_constructed()
void* new_instance() const
{
return new Object();
return new Struct();
}
void delete_instance(void* ptr)
void delete_instance(void* ptr) const
{
delete reinterpret_cast<Object*>(ptr);
delete reinterpret_cast<Struct*>(ptr);
}
};
......@@ -961,10 +1256,20 @@ bool operator!=(const struct_b& lhs, const struct_b& rhs)
return !(lhs == rhs);
}
template<class C, class Parent, typename... Args>
std::pair<C Parent::*, meta_object<C>*> compound_member(C Parent::*c_ptr, const Args&... args)
struct struct_c
{
return std::make_pair(c_ptr, new meta_object<C>(args...));
std::map<std::string, std::u16string> strings;
std::set<int> ints;
};
bool operator==(const struct_c& lhs, const struct_c& rhs)
{
return lhs.strings == rhs.strings && lhs.ints == rhs.ints;
}
bool operator!=(const struct_c& lhs, const struct_c& rhs)
{
return !(lhs == rhs);
}
class string_serializer : public serializer
......@@ -1024,22 +1329,15 @@ class string_serializer : public serializer
out << " )";
}
void begin_list(size_t)
void begin_sequence(size_t)
{
clear();
out << "{ ";
}
void end_list()
void end_sequence()
{
if (!m_after_value)
{
out << "}";
}
else
{
out << " }";
}
out << (m_after_value ? " }" : "}");
}
void write_value(const pt_value& value)
......@@ -1049,8 +1347,21 @@ class string_serializer : public serializer
m_after_value = true;
}
void write_tuple(size_t size, const pt_value* values)
{
clear();
out << " {";
const pt_value* end = values + size;
for ( ; values != end; ++values)
{
write_value(*values);
}
out << (m_after_value ? " }" : "}");
}
};
/*
class xml_serializer : public serializer
{
......@@ -1126,6 +1437,7 @@ class xml_serializer : public serializer
}
};
*/
class binary_serializer : public serializer
{
......@@ -1135,27 +1447,6 @@ class binary_serializer : public serializer
buf_type m_buf;
size_t m_wr_pos;
struct pt_writer
{
buf_type& m_buf;
size_t& m_wr_pos;
pt_writer(buf_type& buf, size_t& pos) : m_buf(buf), m_wr_pos(pos) { }
template<typename T>
void operator()(const T& value)
{
memcpy(m_buf + m_wr_pos, &value, sizeof(T));
m_wr_pos += sizeof(T);
}
void operator()(const std::u16string&) { }
void operator()(const std::u32string&) { }
};
template<typename T>
void write(const T& value)
{
......@@ -1170,6 +1461,39 @@ class binary_serializer : public serializer
m_wr_pos += str.size();
}
void write(const std::u16string& str)
{
write(static_cast<std::uint32_t>(str.size()));
for (char16_t c : str)
{
write(static_cast<std::uint16_t>(c));
}
}
void write(const std::u32string& str)
{
write(static_cast<std::uint32_t>(str.size()));
for (char32_t c : str)
{
write(static_cast<std::uint32_t>(c));
}
}
struct pt_writer
{
binary_serializer* self;
inline pt_writer(binary_serializer* mself) : self(mself) { }
template<typename T>
inline void operator()(const T& value)
{
self->write(value);
}
};
public:
binary_serializer(char* buf) : m_buf(buf), m_wr_pos(0) { }
......@@ -1179,18 +1503,31 @@ class binary_serializer : public serializer
write(tname);
}
void end_object() { }
void end_object()
{
}
void begin_list(size_t list_size)
void begin_sequence(size_t list_size)
{
write(static_cast<std::uint32_t>(list_size));
}
void end_list() { }
void end_sequence()
{
}
void write_value(const pt_value& value)
{
value.apply(pt_writer(m_buf, m_wr_pos));
value.apply(pt_writer(this));
}
void write_tuple(size_t size, const pt_value* values)
{
const pt_value* end = values + size;
for ( ; values != end; ++values)
{
write_value(*values);
}
}
};
......@@ -1252,7 +1589,7 @@ class binary_deserializer : public deserializer
{
CharType c;
read(c);
str_size += static_cast<typename StringType::value_type>(c);
str += static_cast<typename StringType::value_type>(c);
}
}
......@@ -1310,14 +1647,14 @@ class binary_deserializer : public deserializer
{
}
size_t begin_list(primitive_type)
size_t begin_sequence()
{
std::uint32_t size;
read(size);
return size;
}
void end_list()
void end_sequence()
{
}
......@@ -1328,6 +1665,18 @@ class binary_deserializer : public deserializer
return val;
}
void read_tuple(size_t size,
const primitive_type* ptypes,
pt_value* storage)
{
const primitive_type* end = ptypes + size;
for ( ; ptypes != end; ++ptypes)
{
*storage = std::move(read_value(*ptypes));
++storage;
}
}
};
class string_deserializer : public deserializer
......@@ -1440,14 +1789,14 @@ class string_deserializer : public deserializer
}
}
size_t begin_list(primitive_type)
size_t begin_sequence()
{
consume('{');
auto list_end = std::find(m_pos, m_str.end(), '}');
return std::count(m_pos, list_end, ',') + 1;
}
void end_list()
void end_sequence()
{
consume('}');
}
......@@ -1490,10 +1839,23 @@ class string_deserializer : public deserializer
return result;
}
void foo() {}
void read_tuple(size_t size, const primitive_type* begin, pt_value* storage)
{
consume('{');
const primitive_type* end = begin + size;
for ( ; begin != end; ++begin)
{
*storage = std::move(read_value(*begin));
++storage;
}
consume('}');
}
};
template<typename T>
std::string to_string(T* what, meta_object<T>* mobj)
std::string to_string(void* what, meta_type* mobj)
{
std::ostringstream osstr;
string_serializer strs(osstr);
......@@ -1501,17 +1863,30 @@ std::string to_string(T* what, meta_object<T>* mobj)
return osstr.str();
}
std::size_t test__serialization()
template<class C, class Parent, typename... Args>
std::pair<C Parent::*, meta_struct<C>*>
compound_member(C Parent::*c_ptr, const Args&... args)
{
return std::make_pair(c_ptr, new meta_struct<C>(args...));
}
template<class C, typename... Args>
meta_type* meta_object(const Args&... args)
{
return new meta_struct<C>(args...);
}
std::size_t test__serialization()
{
CPPA_TEST(test__serialization);
CPPA_CHECK_EQUAL((trait::is_iterable<int>::value), false);
CPPA_CHECK_EQUAL((is_iterable<int>::value), false);
// std::string is primitive and thus not identified by is_iterable
CPPA_CHECK_EQUAL((trait::is_iterable<std::string>::value), false);
CPPA_CHECK_EQUAL((trait::is_iterable<std::list<int>>::value), true);
CPPA_CHECK_EQUAL((trait::is_iterable<std::map<int,int>>::value), true);
CPPA_CHECK_EQUAL((is_iterable<std::string>::value), false);
CPPA_CHECK_EQUAL((is_iterable<std::list<int>>::value), true);
CPPA_CHECK_EQUAL((is_iterable<std::map<int,int>>::value), true);
// test pt_value implementation
{
pt_value v1(42);
......@@ -1522,69 +1897,99 @@ std::size_t test__serialization()
// type mismatch => unequal
CPPA_CHECK(v2 != static_cast<std::int8_t>(42));
}
root_object root;
// test serializers / deserializers
{
meta_object<struct_b> meta_b {
compound_member(&struct_b::a, &struct_a::x, &struct_a::y),
&struct_b::z,
&struct_b::ints
};
// test meta_struct implementation for primitive types
{
meta_struct<std::uint32_t> meta_int;
auto i = meta_int.new_instance();
auto str = to_string(i, &meta_int);
meta_int.delete_instance(i);
cout << "str: " << str << endl;
}
// test serializers / deserializers with struct_b
{
// get meta object for struct_b
auto meta_b = meta_object<struct_b>(compound_member(&struct_b::a,
&struct_a::x,
&struct_a::y),
&struct_b::z,
&struct_b::ints);
// "announce" meta types
s_meta_types["struct_b"] = &meta_b;
s_meta_types["struct_b"] = meta_b;
// testees
struct_b b1 = { { 1, 2 }, 3, { 4, 5, 6, 7, 8, 9, 10 } };
struct_b b2;
struct_b b3;
// expected result of to_string(&b1, meta_b)
auto b1str = "struct_b ( struct_a ( 1, 2 ), 3, "
"{ 4, 5, 6, 7, 8, 9, 10 } )";
CPPA_CHECK_EQUAL((to_string(&b1, &meta_b)), b1str);
// verify
CPPA_CHECK_EQUAL((to_string(&b1, meta_b)), b1str);
// binary buffer
char buf[512];
// serialize b1 to buf
{
binary_serializer bs(buf);
meta_b.serialize(&b1, &bs);
meta_b->serialize(&b1, &bs);
}
// deserialize b2 from buf
{
binary_deserializer bd(buf, 512);
auto res = root.deserialize(&bd);
CPPA_CHECK_EQUAL(res.second, &meta_b);
if (res.second == &meta_b)
CPPA_CHECK_EQUAL(res.second, meta_b);
if (res.second == meta_b)
{
b2 = *reinterpret_cast<struct_b*>(res.first);
}
res.second->delete_instance(res.first);
}
// verify result of serialization / deserialization
CPPA_CHECK_EQUAL(b1, b2);
CPPA_CHECK_EQUAL((to_string(&b2, &meta_b)), b1str);
CPPA_CHECK_EQUAL((to_string(&b2, meta_b)), b1str);
// deserialize b3 from string, using string_deserializer
{
string_deserializer strd(b1str);
auto res = root.deserialize(&strd);
CPPA_CHECK_EQUAL(res.second, &meta_b);
if (res.second == &meta_b)
CPPA_CHECK_EQUAL(res.second, meta_b);
if (res.second == meta_b)
{
b3 = *reinterpret_cast<struct_b*>(res.first);
}
res.second->delete_instance(res.first);
}
CPPA_CHECK_EQUAL(b1, b3);
// cleanup
s_meta_types.clear();
delete meta_b;
}
// test serializers / deserializers with struct_c
{
// get meta type of struct_c and "announce"
auto meta_c = meta_object<struct_c>(&struct_c::strings,&struct_c::ints);
s_meta_types["struct_c"] = meta_c;
// testees
struct_c c1 = { { { "abc", u"cba" }, { "x", u"y" } }, { 9, 4, 5 } };
struct_c c2;
// binary buffer
char buf[512];
// serialize c1 to buf
{
binary_serializer bs(buf);
meta_c->serialize(&c1, &bs);
}
// serialize c2 from buf
{
binary_deserializer bd(buf, 512);
auto res = root.deserialize(&bd);
CPPA_CHECK_EQUAL(res.second, meta_c);
if (res.second == meta_c)
{
c2 = *reinterpret_cast<struct_c*>(res.first);
}
res.second->delete_instance(res.first);
}
// verify result of serialization / deserialization
CPPA_CHECK_EQUAL(c1, c2);
}
return CPPA_TEST_RESULT;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment