Skip to content

Instantly share code, notes, and snippets.

@pramodswainn
Forked from jamboree/cppon.cpp
Created March 30, 2023 10:29
Show Gist options
  • Save pramodswainn/ebe07dd31bc33d5bd1405668974851a8 to your computer and use it in GitHub Desktop.
Save pramodswainn/ebe07dd31bc33d5bd1405668974851a8 to your computer and use it in GitHub Desktop.
#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