-
-
Save pramodswainn/ebe07dd31bc33d5bd1405668974851a8 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 <cstdlib> | |
#include <type_traits> | |
#include <iostream> | |
#include <memory> | |
#include <string> | |
#include <vector> | |
#include <map> | |
#include <boost/numeric/conversion/cast.hpp> | |
#include <boost/lexical_cast.hpp> | |
#include <boost/utility/string_ref.hpp> | |
namespace cppon | |
{ | |
struct binary | |
{ | |
binary(void* data, std::size_t bytes) | |
: _data(new std::uint8_t[bytes]), _bytes(bytes) | |
{ | |
std::memcpy(_data.get(), data, bytes); | |
} | |
binary(binary&&) = default; | |
binary(binary const& other) | |
: _data(new std::uint8_t[other._bytes]), _bytes(other._bytes) | |
{ | |
std::memcpy(_data.get(), other._data.get(), _bytes); | |
} | |
binary& operator=(binary other) noexcept | |
{ | |
return *new(this) binary(std::move(other)); | |
} | |
void* data() const | |
{ | |
return _data.get(); | |
} | |
std::size_t size() const | |
{ | |
return _bytes; | |
} | |
private: | |
std::unique_ptr<std::uint8_t[]> _data; | |
std::size_t _bytes; | |
}; | |
struct binary_view | |
{ | |
binary_view(void* data, std::size_t bytes) | |
: _data(data), _bytes(bytes) | |
{} | |
binary_view(binary& buf) | |
: _data(buf.data()), _bytes(buf.size()) | |
{} | |
void* data() const | |
{ | |
return _data; | |
} | |
std::size_t size() const | |
{ | |
return _bytes; | |
} | |
private: | |
void* _data; | |
std::size_t _bytes; | |
}; | |
struct const_binary_view | |
{ | |
const_binary_view(void const* data, std::size_t bytes) | |
: _data(data), _bytes(bytes) | |
{} | |
const_binary_view(binary& buf) | |
: _data(buf.data()), _bytes(buf.size()) | |
{} | |
const_binary_view(binary_view const& buf) | |
: _data(buf.data()), _bytes(buf.size()) | |
{} | |
void const* data() const | |
{ | |
return _data; | |
} | |
std::size_t size() const | |
{ | |
return _bytes; | |
} | |
private: | |
void const* _data; | |
std::size_t _bytes; | |
}; | |
inline binary_view make_binary_view(void* data, std::size_t bytes) | |
{ | |
return {data, bytes}; | |
} | |
inline const_binary_view make_binary_view(void const* data, std::size_t bytes) | |
{ | |
return {data, bytes}; | |
} | |
} | |
namespace cppon { namespace detail | |
{ | |
struct key_cmp | |
{ | |
using is_transparent = std::true_type; | |
template<class T, class U> | |
bool operator()(T const& a, U const& b) const | |
{ | |
return a < b; | |
} | |
}; | |
struct assign_policy | |
{ | |
template<class T, class U> | |
static void call(T& t, U&& u) | |
{ | |
t = std::forward<U>(u); | |
} | |
}; | |
struct construct_policy | |
{ | |
template<class T, class U> | |
static void call(T& t, U&& u) | |
{ | |
new(&t) T(std::forward<U>(u)); | |
} | |
}; | |
template<class Policy> | |
struct transfer | |
{ | |
void* dst; | |
template<class T> | |
void operator()(T& t) const | |
{ | |
Policy::call(*static_cast<std::remove_const_t<T>*>(dst), std::move(t)); | |
} | |
void operator()(const_binary_view const& buf) const | |
{ | |
auto data = static_cast<std::uint8_t const*>(buf.data()); | |
std::memcpy(dst, data - 1, buf.size() + 1); | |
} | |
}; | |
struct destroyer | |
{ | |
template<class T> | |
void operator()(T& t) const | |
{ | |
t.~T(); | |
} | |
void operator()(binary_view const& buf) const {} | |
}; | |
using construct_mover = transfer<construct_policy>; | |
using construct_copyer = transfer<construct_policy>; | |
using assign_mover = transfer<assign_policy>; | |
using assign_copyer = transfer<assign_policy>; | |
struct fallback | |
{ | |
template<class T> | |
fallback(T const&) {} | |
}; | |
}} | |
namespace cppon | |
{ | |
enum class tag// : std::uint8_t | |
{ | |
null, | |
integer, | |
floating_point, | |
boolean, | |
small_binary, | |
binary, | |
string, | |
array, | |
object, | |
}; | |
class value; | |
using array = std::vector<value>; | |
using object = std::map<std::string, value, detail::key_cmp>; | |
class value | |
{ | |
union storage | |
{ | |
std::uint8_t small_binary[1]; | |
std::nullptr_t null; | |
std::int64_t integer; | |
double floating_point; | |
bool boolean; | |
std::string string; | |
cppon::binary binary; | |
cppon::array array; | |
cppon::object object; | |
storage() {} | |
~storage() {} | |
}; | |
tag _tag; | |
storage _storage; | |
public: | |
value(std::nullptr_t) noexcept : _tag(tag::null) | |
{ | |
new(&_storage.null) std::nullptr_t(); | |
} | |
template<class T, std::enable_if_t<std::is_integral<T>::value, bool> = true> | |
value(T val) noexcept : _tag(tag::integer) | |
{ | |
new(&_storage.integer) std::int64_t(val); | |
} | |
value(double val) noexcept : _tag(tag::floating_point) | |
{ | |
new(&_storage.floating_point) double(val); | |
} | |
value(bool val) noexcept : _tag(tag::boolean) | |
{ | |
new(&_storage.boolean) bool(val); | |
} | |
value(char const* val) noexcept : _tag(tag::string) | |
{ | |
new(&_storage.string) std::string(val); | |
} | |
value(std::string val) noexcept : _tag(tag::string) | |
{ | |
new(&_storage.string) std::string(std::move(val)); | |
} | |
value(array val) noexcept : _tag(tag::array) | |
{ | |
new(&_storage.array) array(std::move(val)); | |
} | |
value(object val) noexcept : _tag(tag::object) | |
{ | |
new(&_storage.object) object(std::move(val)); | |
} | |
value(void* data, std::size_t bytes) noexcept | |
{ | |
static_assert(sizeof(storage) <= 256, "small_binary too large"); | |
if (bytes < sizeof(storage)) | |
{ | |
_tag = tag::small_binary; | |
*_storage.small_binary = static_cast<std::uint8_t>(bytes); | |
std::memcpy(_storage.small_binary + 1, data, bytes); | |
} | |
else | |
{ | |
_tag = tag::binary; | |
new(&_storage.binary) binary(data, bytes); | |
} | |
} | |
template<class T, class F> | |
static auto apply(T& self, F&& f) | |
{ | |
switch (self._tag) | |
{ | |
case cppon::tag::null: | |
return f(self._storage.null); | |
case cppon::tag::integer: | |
return f(self._storage.integer); | |
case cppon::tag::floating_point: | |
return f(self._storage.floating_point); | |
case cppon::tag::boolean: | |
return f(self._storage.boolean); | |
case cppon::tag::small_binary: | |
return f(make_binary_view(self._storage.small_binary + 1, *self._storage.small_binary)); | |
case cppon::tag::binary: | |
return f(self._storage.binary); | |
case cppon::tag::string: | |
return f(self._storage.string); | |
case cppon::tag::array: | |
return f(self._storage.array); | |
case cppon::tag::object: | |
return f(self._storage.object); | |
} | |
} | |
value(value&& other) noexcept : _tag(other._tag) | |
{ | |
apply(other, detail::construct_mover{&_storage}); | |
} | |
value(value const& other) : _tag(other._tag) | |
{ | |
apply(other, detail::construct_copyer{&_storage}); | |
} | |
value& operator=(value&& other) noexcept | |
{ | |
apply(other, detail::assign_mover{&_storage}); | |
return *this; | |
} | |
value& operator=(value const& other) | |
{ | |
apply(other, detail::assign_copyer{&_storage}); | |
return *this; | |
} | |
~value() | |
{ | |
apply(*this, detail::destroyer{}); | |
} | |
template<class T> | |
void extract(T& t) const; | |
}; | |
template<class T> | |
inline std::pair<char const*, T&> member(char const* name, T& data) | |
{ | |
return {name, data}; | |
} | |
} | |
namespace cppon { namespace extract_detail | |
{ | |
template<class Expr, class T = void> | |
struct enable_if_valid | |
{ | |
using type = T; | |
}; | |
template<class T, class U = void> | |
using enable_if_valid_t = typename enable_if_valid<T, U>::type; | |
inline value const& find(object const& obj, char const* name) | |
{ | |
auto it = obj.find(name); | |
if (it == obj.end()) | |
throw std::runtime_error("member not found"); | |
return it->second; | |
} | |
template<class T, class F> | |
inline auto object_layout(T& t, F&& f) -> decltype(t.object_layout(std::forward<F>(f))) | |
{ | |
return t.object_layout(std::forward<F>(f)); | |
} | |
struct extract_each | |
{ | |
object const& obj; | |
template<class... T> | |
void operator()(T&&... member) const | |
{ | |
std::initializer_list<bool> | |
{ | |
(find(obj, member.first).extract(member.second), true)... | |
}; | |
} | |
}; | |
using std::to_string; | |
template<class T> | |
inline void extract_from(T& dst, T const& src) | |
{ | |
dst = src; | |
} | |
template<class T, class U> | |
inline auto extract_from(T& dst, U const& src) -> std::enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> | |
{ | |
dst = boost::numeric_cast<T>(src); | |
} | |
template<class T> | |
inline auto extract_from(T& dst, std::string const& src) -> std::enable_if_t<std::is_floating_point<T>::value> | |
{ | |
dst = boost::lexical_cast<T>(src); | |
} | |
template<class T> | |
inline auto extract_from(T& dst, std::string const& src) -> std::enable_if_t<std::is_integral<T>::value> | |
{ | |
try | |
{ | |
dst = boost::lexical_cast<T>(src); | |
} | |
catch (boost::bad_lexical_cast const&) | |
{ | |
dst = boost::numeric_cast<T>(boost::lexical_cast<double>(src)); | |
} | |
} | |
template<class T> | |
inline auto extract_from(std::string& dst, T const& src) -> enable_if_valid_t<decltype(to_string(src))> | |
{ | |
dst = to_string(src); | |
} | |
template<class T> | |
inline auto extract_from(T& dst, object const& src) -> decltype(object_layout(dst, extract_each{src})) | |
{ | |
object_layout(dst, extract_each{src}); | |
} | |
template<class T> | |
struct is_static_sequence : std::false_type {}; | |
template<class T, std::size_t N> | |
struct is_static_sequence<T[N]> : std::true_type {}; | |
template<class T> | |
struct is_dynamic_sequence : std::false_type {}; | |
template<class T> | |
struct is_dynamic_sequence<std::vector<T>> : std::true_type {}; | |
template<class T> | |
inline auto extract_from(T& dst, array const& src) -> std::enable_if_t<is_static_sequence<T>::value> | |
{ | |
using std::size; | |
using std::begin; | |
if (size(dst) != src.size()) | |
throw std::runtime_error("size mismatched"); | |
auto it = begin(dst); | |
for (auto const& obj : src) | |
{ | |
obj.extract(*it); | |
++it; | |
} | |
} | |
template<class T> | |
inline auto extract_from(T& dst, array const& src) -> std::enable_if_t<is_dynamic_sequence<T>::value> | |
{ | |
typename T::value_type temp; | |
for (auto const& obj : src) | |
{ | |
obj.extract(temp); | |
dst.push_back(std::move(temp)); | |
} | |
} | |
struct extract_from_fn | |
{ | |
template<class T, class U> | |
auto operator()(T& dst, U const& src) const -> | |
decltype(extract_from(dst, src)) | |
{ | |
extract_from(dst, src); | |
} | |
}; | |
}} | |
namespace cppon | |
{ | |
constexpr extract_detail::extract_from_fn extract_from{}; | |
namespace detail | |
{ | |
template<class T> | |
struct extractor | |
{ | |
T& result; | |
template<class U> | |
auto operator()(U const& val) -> decltype(extract_from(result, val)) | |
{ | |
extract_from(result, val); | |
} | |
void operator()(fallback) const | |
{ | |
throw std::bad_cast(); | |
} | |
}; | |
} | |
template<class T> | |
void value::extract(T& t) const | |
{ | |
apply(*this, detail::extractor<T>{t}); | |
} | |
} | |
struct A | |
{ | |
int i; | |
float f; | |
}; | |
struct C | |
{ | |
std::vector<A> as; | |
std::string name; | |
}; | |
template<class F> | |
inline auto object_layout(A& self, F&& layout) | |
{ | |
return layout | |
( | |
cppon::member("i", self.i), | |
cppon::member("f", self.f) | |
); | |
} | |
template<class F> | |
auto object_layout(C& self, F&& layout) | |
{ | |
return layout | |
( | |
cppon::member("as", self.as), | |
cppon::member("name", self.name) | |
); | |
} | |
int main(int argc, char *argv[]) | |
{ | |
cppon::value v | |
{ | |
cppon::object | |
{ | |
{"name", "neko"}, | |
{ | |
"as", cppon::array | |
{ | |
cppon::object{{"i", 99}, {"f", "30.5e2"}}, | |
cppon::object{{"i", "2e3"}, {"f", 4000}} | |
} | |
} | |
} | |
}; | |
//cppon::value v2(v); | |
C c; | |
try | |
{ | |
v.extract(c); | |
std::cout << c.name << "\n"; | |
for (auto&& a : c.as) | |
std::cout << "(" << a.f << ", " << a.i << ")\n"; | |
//std::cout << a.a << " : " << a.b; | |
} | |
catch (std::exception& e) | |
{ | |
std::cout << e.what(); | |
} | |
//std::bad_cast() | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment