-
-
Save pdimov/c74627565d35cdff97d7f4d0ae7a7b00 to your computer and use it in GitHub Desktop.
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 <boost/json.hpp> | |
#include <boost/json/basic_parser_impl.hpp> | |
#include <boost/mp11.hpp> | |
#include <boost/describe.hpp> | |
// parse_error | |
BOOST_DEFINE_ENUM_CLASS(parse_error, | |
success, | |
extra_data, | |
expected_integer, | |
expected_number, | |
expected_string, | |
expected_bool, | |
expected_array, | |
expected_object, | |
number_out_of_range, | |
invalid_array_size, | |
invalid_member_name | |
) | |
namespace boost | |
{ | |
namespace system | |
{ | |
template<> struct is_error_code_enum<parse_error>: std::true_type | |
{ | |
}; | |
} // namespace system | |
} // namespace boost | |
struct parse_error_category_impl: public boost::system::error_category | |
{ | |
const char * name() const BOOST_NOEXCEPT | |
{ | |
return "json_parse_error"; | |
} | |
std::string message( int ev ) const | |
{ | |
switch( static_cast<parse_error>( ev ) ) | |
{ | |
case parse_error::extra_data: return "Extra data"; | |
case parse_error::expected_integer: return "Expected integer"; | |
case parse_error::expected_number: return "Expected number"; | |
case parse_error::expected_string: return "Expected string"; | |
case parse_error::expected_bool: return "Expected boolean"; | |
case parse_error::expected_array: return "Expected array"; | |
case parse_error::expected_object: return "Expected object"; | |
case parse_error::number_out_of_range: return "Number out of range"; | |
case parse_error::invalid_array_size: return "Invalid array size"; | |
case parse_error::invalid_member_name: return "Invalid member name"; | |
default: return "Unknown error"; | |
} | |
} | |
}; | |
boost::system::error_category const& parse_error_category() | |
{ | |
static constexpr parse_error_category_impl instance{}; | |
return instance; | |
} | |
boost::system::error_code make_error_code( parse_error e ) | |
{ | |
return boost::system::error_code( static_cast<int>( e ), parse_error_category() ); | |
} | |
// handler_base | |
#define FAIL(ec, e) static constexpr auto loc = BOOST_CURRENT_LOCATION; ec.assign( e, &loc ) | |
template<parse_error E> class handler_error_base | |
{ | |
public: | |
handler_error_base() = default; | |
handler_error_base( handler_error_base const& ) = delete; | |
handler_error_base& operator=( handler_error_base const& ) = delete; | |
public: | |
bool on_object_begin( boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_object_end( std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_array_begin( boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_array_end( std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_key_part( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_key( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_string_part( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_string( boost::json::string_view, std::size_t, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_number_part( boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_int64( std::int64_t, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_uint64( std::uint64_t, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_double( double, boost::json::string_view, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_bool( bool, boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
bool on_null( boost::json::error_code& ec ) { FAIL( ec, E ); return false; } | |
}; | |
// integral_handler | |
template<class V, class P> class integral_handler: public handler_error_base<parse_error::expected_integer> | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
public: | |
integral_handler( integral_handler const& ) = delete; | |
integral_handler& operator=( integral_handler const& ) = delete; | |
public: | |
integral_handler( V* v, P* p ): value_( v ), parent_( p ) | |
{ | |
} | |
bool on_number_part( boost::json::string_view, boost::json::error_code& ) | |
{ | |
return true; | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view, boost::json::error_code& ec ) | |
{ | |
if( v < std::numeric_limits<V>::min() || v > std::numeric_limits<V>::max() ) | |
{ | |
FAIL( ec, parse_error::number_out_of_range ); | |
return false; | |
} | |
*value_ = static_cast<V>( v ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view, boost::json::error_code& ec ) | |
{ | |
if( v > std::numeric_limits<V>::max() ) | |
{ | |
FAIL( ec, parse_error::number_out_of_range ); | |
return false; | |
} | |
*value_ = static_cast<V>( v ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_array_end( std::size_t, boost::json::error_code& ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
}; | |
// floating_point_handler | |
template<class V, class P> class floating_point_handler: public handler_error_base<parse_error::expected_number> | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
public: | |
floating_point_handler( floating_point_handler const& ) = delete; | |
floating_point_handler& operator=( floating_point_handler const& ) = delete; | |
public: | |
floating_point_handler( V* v, P* p ): value_( v ), parent_( p ) | |
{ | |
} | |
bool on_number_part( boost::json::string_view, boost::json::error_code& ) | |
{ | |
return true; | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view, boost::json::error_code& ) | |
{ | |
*value_ = static_cast<V>( v ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view, boost::json::error_code& ) | |
{ | |
*value_ = static_cast<V>( v ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_double( double v, boost::json::string_view, boost::json::error_code& ) | |
{ | |
*value_ = static_cast<V>( v ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_array_end( std::size_t, boost::json::error_code& ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
}; | |
// string_handler | |
template<class V, class P> class string_handler: public handler_error_base<parse_error::expected_string> | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
public: | |
string_handler( V* v, P* p ): value_( v ), parent_( p ) | |
{ | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t, boost::json::error_code& ) | |
{ | |
value_->append( sv.begin(), sv.end() ); | |
return true; | |
} | |
bool on_string( boost::json::string_view sv, std::size_t, boost::json::error_code& ) | |
{ | |
value_->append( sv.begin(), sv.end() ); | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_array_end( std::size_t, boost::json::error_code& ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
}; | |
// bool_handler | |
template<class V, class P> class bool_handler: public handler_error_base<parse_error::expected_bool> | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
public: | |
bool_handler( bool_handler const& ) = delete; | |
bool_handler& operator=( bool_handler const& ) = delete; | |
public: | |
bool_handler( V* v, P* p ): value_( v ), parent_( p ) | |
{ | |
} | |
bool on_bool( bool v, boost::json::error_code& ) | |
{ | |
*value_ = v; | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_array_end( std::size_t, boost::json::error_code& ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
}; | |
// forward declarations | |
template<class V, class P> class sequence_handler; | |
template<class V, class P> class map_handler; | |
template<class V, class P> class tuple_handler; | |
template<class V, class P> class described_struct_handler; | |
template<class V> struct unknown_type_handler | |
{ | |
static_assert( sizeof(V) == 0, "This type is not supported" ); | |
}; | |
// is_string | |
template<class T> struct is_string: std::is_same<T, std::string> | |
{ | |
}; | |
// is_map | |
template<class T, class E = void> struct is_map: std::false_type | |
{ | |
}; | |
template<class T> struct is_map<T, decltype( std::declval<T&>().emplace( std::declval<std::string>(), std::declval<typename T::mapped_type>() ), (void)0 )>: std::true_type | |
{ | |
}; | |
// is_sequence | |
template<class T, class E = void> struct is_sequence: std::false_type | |
{ | |
}; | |
template<class T> struct is_sequence<T, decltype( std::declval<T&>().push_back( std::declval<typename T::value_type>() ), (void)0 )>: std::true_type | |
{ | |
}; | |
// is_tuple | |
template<class T, class E = void> struct is_tuple: std::false_type | |
{ | |
}; | |
template<class T> struct is_tuple<T, decltype( (void)std::tuple_size<T>::value )>: std::true_type | |
{ | |
}; | |
// is_described_struct | |
template<class T, class E = void> struct is_described_struct: std::false_type | |
{ | |
}; | |
template<class T> struct is_described_struct<T, decltype((void)boost::describe::describe_members<T, boost::describe::mod_any_access>())>: std::true_type | |
{ | |
}; | |
// get_handler | |
template<class V, class P> using get_handler = boost::mp11::mp_cond< | |
std::is_same<V, bool>, bool_handler<V, P>, | |
std::is_integral<V>, integral_handler<V, P>, | |
std::is_floating_point<V>, floating_point_handler<V, P>, | |
is_string<V>, string_handler<V, P>, | |
is_map<V>, map_handler<V, P>, | |
is_sequence<V>, sequence_handler<V, P>, | |
is_tuple<V>, tuple_handler<V, P>, | |
is_described_struct<V>, described_struct_handler<V, P>, | |
boost::mp11::mp_true, unknown_type_handler<V> | |
>; | |
// sequence_handler | |
template<class V, class P> class sequence_handler | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
using value_type = typename V::value_type; | |
using inner_handler_type = get_handler<value_type, sequence_handler>; | |
value_type next_value_ = {}; | |
inner_handler_type inner_; | |
bool inner_active_ = false; | |
public: | |
sequence_handler( sequence_handler const& ) = delete; | |
sequence_handler& operator=( sequence_handler const& ) = delete; | |
public: | |
sequence_handler( V* v, P* p ): value_( v ), parent_( p ), inner_( &next_value_, this ) | |
{ | |
} | |
void signal_value() | |
{ | |
value_->push_back( std::move( next_value_ ) ); | |
next_value_ = {}; | |
} | |
void signal_end() | |
{ | |
inner_active_ = false; | |
parent_->signal_value(); | |
} | |
#define INVOKE_INNER(f) if( !inner_active_ ) { FAIL( ec, parse_error::expected_array ); return false; } else return inner_.f | |
bool on_object_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_begin( ec ) ); | |
} | |
bool on_object_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_end( n, ec ) ); | |
} | |
bool on_array_begin( boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_array_begin( ec ); | |
} | |
else | |
{ | |
inner_active_ = true; | |
return true; | |
} | |
} | |
bool on_array_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_array_end( n, ec ); | |
} | |
else | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
} | |
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key_part( sv, n, ec ) ); | |
} | |
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key( sv, n, ec ) ); | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string_part( sv, n, ec ) ); | |
} | |
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string( sv, n, ec ) ); | |
} | |
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_number_part( sv, ec ) ); | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_int64( v, sv, ec ) ); | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_uint64( v, sv, ec ) ); | |
} | |
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_double( v, sv, ec ) ); | |
} | |
bool on_bool( bool v, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_bool( v, ec ) ); | |
} | |
bool on_null( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_null( ec ) ); | |
} | |
#undef INVOKE_INNER | |
}; | |
// map_handler | |
template<class V, class P> class map_handler | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
using mapped_type = typename V::mapped_type; | |
using inner_handler_type = get_handler<mapped_type, map_handler>; | |
std::string key_; | |
mapped_type next_value_ = {}; | |
inner_handler_type inner_; | |
bool inner_active_ = false; | |
public: | |
map_handler( map_handler const& ) = delete; | |
map_handler& operator=( map_handler const& ) = delete; | |
public: | |
map_handler( V* v, P* p ): value_( v ), parent_( p ), inner_( &next_value_, this ) | |
{ | |
} | |
void signal_value() | |
{ | |
value_->emplace( std::move( key_ ), std::move( next_value_ ) ); | |
key_ = {}; | |
next_value_ = {}; | |
inner_active_ = false; | |
} | |
void signal_end() | |
{ | |
inner_active_ = false; | |
parent_->signal_value(); | |
} | |
#define INVOKE_INNER(f) if( !inner_active_ ) { FAIL( ec, parse_error::expected_object ); return false; } else return inner_.f | |
bool on_object_begin( boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_object_begin( ec ); | |
} | |
return true; | |
} | |
bool on_object_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_object_end( n, ec ); | |
} | |
parent_->signal_value(); | |
return true; | |
} | |
bool on_array_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_array_begin( ec ) ); | |
} | |
bool on_array_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_array_end( n, ec ); | |
} | |
parent_->signal_end(); | |
return true; | |
} | |
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_key_part( sv, n, ec ); | |
} | |
key_.append( sv.data(), sv.size() ); | |
return true; | |
} | |
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ ) | |
{ | |
return inner_.on_key( sv, n, ec ); | |
} | |
key_.append( sv.data(), sv.size() ); | |
inner_active_ = true; | |
return true; | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string_part( sv, n, ec ) ); | |
} | |
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string( sv, n, ec ) ); | |
} | |
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_number_part( sv, ec ) ); | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_int64( v, sv, ec ) ); | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_uint64( v, sv, ec ) ); | |
} | |
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_double( v, sv, ec ) ); | |
} | |
bool on_bool( bool v, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_bool( v, ec ) ); | |
} | |
bool on_null( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_null( ec ) ); | |
} | |
#undef INVOKE_INNER | |
}; | |
// handler tuple | |
template<std::size_t I, class T> struct handler_tuple_element | |
{ | |
T t_; | |
}; | |
template<class S, class... T> struct handler_tuple_impl; | |
template<std::size_t... I, class... T> struct handler_tuple_impl<boost::mp11::index_sequence<I...>, T...>: handler_tuple_element<I, T>... | |
{ | |
}; | |
template<class P, class... V> struct handler_tuple: public handler_tuple_impl<boost::mp11::index_sequence_for<V...>, get_handler<V, P>...> | |
{ | |
using base_type = handler_tuple_impl<boost::mp11::index_sequence_for<V...>, get_handler<V, P>...>; | |
template<class... A> handler_tuple( A... a ): base_type{ { { a.first, a.second } }... } | |
{ | |
} | |
handler_tuple( handler_tuple const& ) = delete; | |
handler_tuple& operator=( handler_tuple const& ) = delete; | |
}; | |
template<std::size_t I, class T> T& get( handler_tuple_element<I, T>& e ) | |
{ | |
return e.t_; | |
} | |
// tuple_handler | |
template<class P, class T> struct tuple_inner_handlers; | |
template<class P, template<class...> class L, class... V> struct tuple_inner_handlers<P, L<V...>> | |
{ | |
handler_tuple<P, V...> handlers_; | |
template<std::size_t... I> tuple_inner_handlers( L<V...>* pv, P* pp, boost::mp11::index_sequence<I...> ): handlers_( std::make_pair( &get<I>(*pv), pp )... ) | |
{ | |
} | |
}; | |
template<class T, class P> class tuple_handler | |
{ | |
private: | |
T * value_; | |
P * parent_; | |
std::string key_; | |
tuple_inner_handlers<tuple_handler, T> inner_; | |
int inner_active_ = -1; | |
public: | |
tuple_handler( tuple_handler const& ) = delete; | |
tuple_handler& operator=( tuple_handler const& ) = delete; | |
public: | |
tuple_handler( T* v, P* p ): value_( v ), parent_( p ), inner_( v, this, boost::mp11::make_index_sequence< std::tuple_size<T>::value >() ) | |
{ | |
} | |
void signal_value() | |
{ | |
++inner_active_; | |
} | |
void signal_end() | |
{ | |
inner_active_ = -1; | |
parent_->signal_value(); | |
} | |
#define INVOKE_INNER(fn) \ | |
if( inner_active_ < 0 ) \ | |
{ \ | |
FAIL( ec, parse_error::expected_array ); \ | |
return false; \ | |
} \ | |
constexpr std::size_t N = std::tuple_size<T>::value; \ | |
if( inner_active_ >= N ) \ | |
{ \ | |
FAIL( ec, parse_error::invalid_array_size ); \ | |
return false; \ | |
} \ | |
return boost::mp11::mp_with_index<N>( inner_active_, [&](auto I){ \ | |
return get<I>( inner_.handlers_ ).fn; \ | |
}); | |
bool on_object_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_begin( ec ) ); | |
} | |
bool on_object_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_end( n, ec ) ); | |
} | |
bool on_array_begin( boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
inner_active_ = 0; | |
return true; | |
} | |
constexpr std::size_t N = std::tuple_size<T>::value; | |
if( inner_active_ >= N ) | |
{ | |
inner_active_ = 0; | |
return true; | |
} | |
return boost::mp11::mp_with_index<N>( inner_active_, [&](auto I){ | |
return get<I>( inner_.handlers_ ).on_array_begin( ec ); | |
}); | |
} | |
bool on_array_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
constexpr std::size_t N = std::tuple_size<T>::value; | |
if( inner_active_ >= N ) | |
{ | |
parent_->signal_value(); | |
return true; | |
} | |
return boost::mp11::mp_with_index<N>( inner_active_, [&](auto I){ | |
return get<I>( inner_.handlers_ ).on_array_end( n, ec ); | |
}); | |
} | |
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key_part( sv, n, ec ) ); | |
} | |
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key( sv, n, ec ) ); | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string_part( sv, n, ec ) ); | |
} | |
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string( sv, n, ec ) ); | |
} | |
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_number_part( sv, ec ) ); | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_int64( v, sv, ec ) ); | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_uint64( v, sv, ec ) ); | |
} | |
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_double( v, sv, ec ) ); | |
} | |
bool on_bool( bool v, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_bool( v, ec ) ); | |
} | |
bool on_null( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_null( ec ) ); | |
} | |
#undef INVOKE_INNER | |
}; | |
// described_struct_handler | |
template<class T, class D> using struct_member_type = std::remove_reference_t< decltype( std::declval<T&>().*D::pointer ) >; | |
template<class P, class T, class L> struct struct_inner_handlers; | |
template<class P, class T, template<class...> class L, class... D> struct struct_inner_handlers<P, T, L<D...>> | |
{ | |
handler_tuple<P, struct_member_type<T, D>...> handlers_; | |
struct_inner_handlers( T* pv, P* pp ): handlers_( std::make_pair( &(pv->*D::pointer), pp )... ) | |
{ | |
} | |
}; | |
template<class V, class P> class described_struct_handler | |
{ | |
private: | |
V * value_; | |
P * parent_; | |
std::string key_; | |
using Dm = boost::describe::describe_members<V, boost::describe::mod_public>; | |
struct_inner_handlers<described_struct_handler, V, Dm> inner_; | |
int inner_active_ = -1; | |
public: | |
described_struct_handler( described_struct_handler const& ) = delete; | |
described_struct_handler& operator=( described_struct_handler const& ) = delete; | |
public: | |
described_struct_handler( V* v, P* p ): value_( v ), parent_( p ), inner_( v, this ) | |
{ | |
} | |
void signal_value() | |
{ | |
key_ = {}; | |
inner_active_ = -1; | |
} | |
void signal_end() | |
{ | |
key_ = {}; | |
inner_active_ = -1; | |
parent_->signal_value(); | |
} | |
#define INVOKE_INNER(fn) \ | |
if( inner_active_ < 0 ) \ | |
{ \ | |
FAIL( ec, parse_error::expected_object ); \ | |
return false; \ | |
} \ | |
return boost::mp11::mp_with_index<boost::mp11::mp_size<Dm>>( inner_active_, [&](auto I){ \ | |
return get<I>( inner_.handlers_ ).fn; \ | |
}); | |
bool on_object_begin( boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
return true; | |
} | |
INVOKE_INNER( on_object_begin( ec ) ); | |
} | |
bool on_object_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
parent_->signal_value(); | |
return true; | |
} | |
INVOKE_INNER( on_object_end( n, ec ) ); | |
} | |
bool on_array_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_array_begin( ec ) ); | |
} | |
bool on_array_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
parent_->signal_end(); | |
return true; | |
} | |
INVOKE_INNER( on_array_end( n, ec ) ); | |
} | |
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ < 0 ) | |
{ | |
key_.append( sv.data(), sv.size() ); | |
return true; | |
} | |
INVOKE_INNER( on_key_part( sv, n, ec ) ); | |
} | |
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
if( inner_active_ >= 0 ) | |
{ | |
INVOKE_INNER( on_key( sv, n, ec ) ); | |
} | |
key_.append( sv.data(), sv.size() ); | |
int i = 0; | |
boost::mp11::mp_for_each<Dm>([&](auto D){ | |
if( key_ == D.name ) | |
{ | |
inner_active_ = i; | |
} | |
++i; | |
}); | |
if( inner_active_ < 0 ) | |
{ | |
FAIL( ec, parse_error::invalid_member_name ); | |
return false; | |
} | |
return true; | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string_part( sv, n, ec ) ); | |
} | |
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string( sv, n, ec ) ); | |
} | |
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_number_part( sv, ec ) ); | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_int64( v, sv, ec ) ); | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_uint64( v, sv, ec ) ); | |
} | |
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_double( v, sv, ec ) ); | |
} | |
bool on_bool( bool v, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_bool( v, ec ) ); | |
} | |
bool on_null( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_null( ec ) ); | |
} | |
#undef INVOKE_INNER | |
}; | |
// handler | |
template<class V> class handler | |
{ | |
private: | |
using inner_handler_type = get_handler<V, handler>; | |
inner_handler_type inner_; | |
bool inner_active_ = true; | |
public: | |
handler( handler const& ) = delete; | |
handler& operator=( handler const& ) = delete; | |
public: | |
constexpr static std::size_t max_object_size = std::size_t(-1); | |
constexpr static std::size_t max_array_size = std::size_t(-1); | |
constexpr static std::size_t max_key_size = std::size_t(-1); | |
constexpr static std::size_t max_string_size = std::size_t(-1); | |
public: | |
explicit handler( V* v ): inner_( v, this ) | |
{ | |
} | |
void signal_value() | |
{ | |
} | |
void signal_end() | |
{ | |
} | |
bool on_document_begin( boost::json::error_code& ) | |
{ | |
return true; | |
} | |
bool on_document_end( boost::json::error_code& ) | |
{ | |
inner_active_ = false; | |
return true; | |
} | |
#define INVOKE_INNER(f) if( !inner_active_ ) { FAIL( ec, parse_error::extra_data ); return false; } else return inner_.f | |
bool on_object_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_begin( ec ) ); | |
} | |
bool on_object_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_object_end( n, ec ) ); | |
} | |
bool on_array_begin( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_array_begin( ec ) ); | |
} | |
bool on_array_end( std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_array_end( n, ec ) ); | |
} | |
bool on_key_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key_part( sv, n, ec ) ); | |
} | |
bool on_key( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_key( sv, n, ec ) ); | |
} | |
bool on_string_part( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string_part( sv, n, ec ) ); | |
} | |
bool on_string( boost::json::string_view sv, std::size_t n, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_string( sv, n, ec ) ); | |
} | |
bool on_number_part( boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_number_part( sv, ec ) ); | |
} | |
bool on_int64( std::int64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_int64( v, sv, ec ) ); | |
} | |
bool on_uint64( std::uint64_t v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_uint64( v, sv, ec ) ); | |
} | |
bool on_double( double v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_double( v, sv, ec ) ); | |
} | |
bool on_bool( bool v, boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_bool( v, ec ) ); | |
} | |
bool on_null( boost::json::error_code& ec ) | |
{ | |
INVOKE_INNER( on_null( ec ) ); | |
} | |
bool on_comment_part(boost::json::string_view, boost::json::error_code&) | |
{ | |
return true; | |
} | |
bool on_comment(boost::json::string_view, boost::json::error_code&) | |
{ | |
return true; | |
} | |
#undef INVOKE_INNER | |
}; | |
// parse_into | |
template<class V> void parse_into( V& v, boost::json::string_view sv, boost::json::error_code& ec ) | |
{ | |
using handler_type = handler<V>; | |
boost::json::basic_parser<handler_type> p_( {}, &v ); | |
std::size_t n = p_.write_some( false, sv.data(), sv.size(), ec ); | |
if( !ec && n < sv.size() ) | |
{ | |
FAIL( ec, parse_error::extra_data ); | |
} | |
} | |
#include <vector> | |
#include <iostream> | |
#include <fstream> | |
#include <iterator> | |
#include <chrono> | |
struct coordinate | |
{ | |
double x{}, y{}, z{}; | |
std::string name; | |
}; | |
BOOST_DESCRIBE_STRUCT(coordinate, (), (x, y, z, name)) | |
struct coordinates | |
{ | |
std::vector<coordinate> coordinates; | |
std::string info; | |
}; | |
BOOST_DESCRIBE_STRUCT(coordinates, (), (coordinates, info)) | |
template<class T, | |
class D1 = boost::describe::describe_members<T, | |
boost::describe::mod_public | boost::describe::mod_protected>, | |
class D2 = boost::describe::describe_members<T, boost::describe::mod_private>, | |
class En = std::enable_if_t<boost::mp11::mp_empty<D2>::value> > | |
void tag_invoke( boost::json::value_from_tag const&, boost::json::value& v, T const & t ) | |
{ | |
auto& obj = v.emplace_object(); | |
boost::mp11::mp_for_each<D1>([&](auto D){ | |
obj[ D.name ] = boost::json::value_from( t.*D.pointer ); | |
}); | |
} | |
template<class T, | |
class Bd = boost::describe::describe_bases<T, boost::describe::mod_any_access>, | |
class Md = boost::describe::describe_members<T, boost::describe::mod_any_access>> | |
bool operator==( T const& t1, T const& t2 ) | |
{ | |
bool r = true; | |
boost::mp11::mp_for_each<Bd>([&](auto D){ | |
using B = typename decltype(D)::type; | |
r = r && (B const&)t1 == (B const&)t2; | |
}); | |
boost::mp11::mp_for_each<Md>([&](auto D){ | |
r = r && t1.*D.pointer == t2.*D.pointer; | |
}); | |
return r; | |
} | |
struct accumulator | |
{ | |
using value_type = coordinate; | |
std::size_t len = 0; | |
double x = 0; | |
double y = 0; | |
double z = 0; | |
void push_back( coordinate const& v ) | |
{ | |
x += v.x; | |
y += v.y; | |
z += v.z; | |
++len; | |
} | |
}; | |
struct coordinates2 | |
{ | |
accumulator coordinates; | |
std::string info; | |
}; | |
BOOST_DESCRIBE_STRUCT(coordinates2, (), (coordinates, info)) | |
using namespace std::chrono_literals; | |
int main() | |
{ | |
// using T = std::map<std::string, int>; | |
// T t1{ { "one", 1 }, { "two", 2 }, { "three", 3 } }; | |
// using T = std::vector< std::vector<int> >; | |
// T t1{ {}, {1}, {2, 3}, {4, 5, 6} }; | |
// using T = std::map<std::string, std::vector<int>>; | |
// T t1{ { "one", {1} }, { "two", {2, 2} }, { "three", {3, 3, 3} } }; | |
// using T = std::map<std::string, std::pair<int, int>>; | |
// T t1{ { "one", { 1, 2 } }, { "two", { 3, 4 } } }; | |
// using T = std::vector<coordinate>; | |
// T t1{ { 1, 1, 1, "1" }, { 2, 2, 2, "2" } }; | |
/* | |
using T = coordinates; | |
T t1{}; | |
std::string json = boost::json::serialize( boost::json::value_from( t1 ) ); | |
std::cout << "json: " << json << std::endl; | |
T t2; | |
boost::json::error_code ec; | |
parse_into( t2, json, ec ); | |
if( ec.failed() ) | |
{ | |
std::cout << "Error: " << ec.what(); | |
return -1; | |
} | |
std::string json2 = boost::json::serialize( boost::json::value_from( t2 ) ); | |
std::cout << "json2: " << json2 << std::endl; | |
if( t1 != t2 ) | |
{ | |
std::cout << "Mismatch!" << std::endl; | |
} | |
else | |
{ | |
std::cout << "Match!" << std::endl; | |
} | |
*/ | |
std::ifstream is( "/tmp/1.json" ); | |
std::string json( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} ); | |
std::cout << "1.json: " << json.size() << " bytes\n"; | |
{ | |
auto tp1 = std::chrono::steady_clock::now(); | |
boost::json::value jv = boost::json::parse( json ); | |
auto tp2 = std::chrono::steady_clock::now(); | |
std::cout << "boost::json::parse: " << (tp2 - tp1) / 1ms << " ms\n"; | |
auto x = 0.0, y = 0.0, z = 0.0; | |
auto len = 0; | |
auto &obj = jv.get_object(); | |
for( auto& v: obj["coordinates"].get_array() ) | |
{ | |
++len; | |
auto& coord = v.get_object(); | |
x += coord["x"].get_double(); | |
y += coord["y"].get_double(); | |
z += coord["z"].get_double(); | |
} | |
x /= len; | |
y /= len; | |
z /= len; | |
auto tp3 = std::chrono::steady_clock::now(); | |
std::cout << " x: " << x << ", y: " << y << ", z: " << z << ": " << (tp3 - tp2) / 1ms << " ms\n"; | |
} | |
{ | |
auto tp1 = std::chrono::steady_clock::now(); | |
coordinates w; | |
boost::json::error_code ec; | |
parse_into( w, json, ec ); | |
if( ec.failed() ) | |
{ | |
std::cout << "Error: " << ec.what() << std::endl; | |
} | |
auto tp2 = std::chrono::steady_clock::now(); | |
std::cout << "parse_into coordinates: " << (tp2 - tp1) / 1ms << " ms\n"; | |
auto x = 0.0, y = 0.0, z = 0.0; | |
auto len = 0; | |
for( auto const& v: w.coordinates ) | |
{ | |
x += v.x; | |
y += v.y; | |
z += v.z; | |
++len; | |
} | |
x /= len; | |
y /= len; | |
z /= len; | |
auto tp3 = std::chrono::steady_clock::now(); | |
std::cout << " x: " << x << ", y: " << y << ", z: " << z << ": " << (tp3 - tp2) / 1ms << " ms\n"; | |
} | |
{ | |
auto tp1 = std::chrono::steady_clock::now(); | |
coordinates2 w; | |
boost::json::error_code ec; | |
parse_into( w, json, ec ); | |
if( ec.failed() ) | |
{ | |
std::cout << "Error: " << ec.what() << std::endl; | |
} | |
auto tp2 = std::chrono::steady_clock::now(); | |
std::cout << "parse_into coordinates2: " << (tp2 - tp1) / 1ms << " ms\n"; | |
double x = w.coordinates.z / w.coordinates.len; | |
double y = w.coordinates.y / w.coordinates.len; | |
double z = w.coordinates.z / w.coordinates.len; | |
auto tp3 = std::chrono::steady_clock::now(); | |
std::cout << " x: " << x << ", y: " << y << ", z: " << z << ": " << (tp3 - tp2) / 1ms << " ms\n"; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment