Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
7761788
Add parse_into
pdimov Oct 21, 2022
76c234b
Add example/parse_into_canada.cpp
pdimov Oct 6, 2021
4c628ae
Add null_handler
pdimov Oct 6, 2021
62af0a0
parse_into header cleanup
grisumbras Oct 19, 2022
56fcb09
reusing existing conversion components
grisumbras Oct 21, 2022
ef6e310
parse_into supports described enums
grisumbras Jul 28, 2023
7133184
parse_into supports optionals
grisumbras Jul 28, 2023
71a6521
extend parse_into API, document it
grisumbras Jul 29, 2023
8e6283a
internal docs for parse_into machinery
grisumbras Aug 11, 2023
9565c71
refactor parse_into tuple handler
grisumbras Aug 13, 2023
b540696
error_code is first argument for parse_into nested handlers' functions
grisumbras Aug 12, 2023
98b85ad
parse_into tuple support works on C++11
grisumbras Aug 15, 2023
7b26a34
parse_into supports variants
grisumbras Aug 6, 2023
10ef306
test/parse_into.cpp requires /bigobj on msvc
grisumbras Aug 15, 2023
9859866
parse_into supports std::array
grisumbras Aug 21, 2023
53b99c8
Add example/parse_into_citm_catalog.cpp
pdimov Oct 7, 2021
4f4396e
test parse_into error reporting
grisumbras Aug 23, 2023
aaa4e7f
add tuple size checking for parse_into
grisumbras Aug 27, 2023
c1b7174
add array size checking for parse_into
grisumbras Aug 27, 2023
f5b9709
add struct member count checking for parse_into
grisumbras Aug 27, 2023
a3821ce
increase parse_into coverage
grisumbras Aug 24, 2023
60bf345
remove unnecessary parameters
grisumbras Sep 9, 2023
34284c3
avoid unnecessary allocations for strings
grisumbras Sep 11, 2023
f144f38
document direct parsing
grisumbras Sep 9, 2023
75981e7
parse_into clears sequences before filling them
grisumbras Sep 30, 2023
e19edde
parse_into clears strings before filling them
grisumbras Oct 2, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
parse_into supports variants
  • Loading branch information
grisumbras committed Oct 9, 2023
commit 7b26a344be8fcd898b35c5b5a4f6527a07c38591
341 changes: 341 additions & 0 deletions include/boost/json/detail/parse_into.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <boost/json/conversion.hpp>
#include <boost/describe/enum_from_string.hpp>

#include <vector>

/*
* This file contains the majority of parse_into functionality, specifically
* the implementation of dedicated handlers for different generic categories of
Expand Down Expand Up @@ -1088,6 +1090,345 @@ class converting_handler<described_class_conversion_tag, V, P>
#endif
};

// variant handler
struct object_begin_handler_event
{ };

struct object_end_handler_event
{ };

struct array_begin_handler_event
{ };

struct array_end_handler_event
{ };

struct key_handler_event
{
std::string value;
};

struct string_handler_event
{
std::string value;
};

struct int64_handler_event
{
std::int64_t value;
};

struct uint64_handler_event
{
std::uint64_t value;
};

struct double_handler_event
{
double value;
};

struct bool_handler_event
{
bool value;
};

struct null_handler_event
{ };

using parse_event = variant2::variant<
object_begin_handler_event,
object_end_handler_event,
array_begin_handler_event,
array_end_handler_event,
key_handler_event,
string_handler_event,
int64_handler_event,
uint64_handler_event,
double_handler_event,
bool_handler_event,
null_handler_event>;

template< class H >
struct event_visitor
{
H& handler;
error_code& ec;

bool
operator()(object_begin_handler_event&) const
{
return handler.on_object_begin(ec);
}

bool
operator()(object_end_handler_event&) const
{
return handler.on_object_end(ec, 0);
}

bool
operator()(array_begin_handler_event&) const
{
return handler.on_array_begin(ec);
}

bool
operator()(array_end_handler_event&) const
{
return handler.on_array_end(ec, 0);
}

bool
operator()(key_handler_event& ev) const
{
return handler.on_key(ec, ev.value, 0);
}

bool
operator()(string_handler_event& ev) const
{
return handler.on_string(ec, ev.value, 0);
}

bool
operator()(int64_handler_event& ev) const
{
return handler.on_int64(ec, ev.value, string_view());
}

bool
operator()(uint64_handler_event& ev) const
{
return handler.on_uint64(ec, ev.value, string_view());
}

bool
operator()(double_handler_event& ev) const
{
return handler.on_double(ec, ev.value, string_view());
}

bool
operator()(bool_handler_event& ev) const
{
return handler.on_bool(ec, ev.value);
}

bool
operator()(null_handler_event&) const
{
return handler.on_null(ec);
}
};

// L<T...> -> variant< monostate, get_handler<T, P>... >
template< class P, class L >
using inner_handler_variant = mp11::mp_push_front<
mp11::mp_transform_q<
mp11::mp_bind_back<get_handler, P>,
mp11::mp_apply<variant2::variant, L>>,
variant2::monostate>;

template< class T, class P >
class converting_handler<variant_conversion_tag, T, P>
{
private:
using variant_size = mp11::mp_size<T>;

T* value_;
P* parent_;

std::string string_;
std::vector< parse_event > events_;
inner_handler_variant<converting_handler, T> inner_;
int inner_active_ = -1;

public:
converting_handler( converting_handler const& ) = delete;
converting_handler& operator=( converting_handler const& ) = delete;

converting_handler( T* v, P* p )
: value_( v )
, parent_( p )
{}

void signal_value()
{
inner_.template emplace<0>();
inner_active_ = -1;
events_.clear();
parent_->signal_value();
}

void signal_end()
{
parent_->signal_end();
}

struct alternative_selector
{
converting_handler* self;

template< class I >
void
operator()( I ) const
{
using V = mp11::mp_at<T, I>;
auto& v = self->value_->template emplace<I::value>( V{} );
self->inner_.template emplace<I::value + 1>(&v, self);
}
};
void
next_alternative()
{
if( ++inner_active_ >= static_cast<int>(variant_size::value) )
return;

mp11::mp_with_index< variant_size::value >(
inner_active_, alternative_selector{this} );
}

struct event_processor
{
converting_handler* self;
error_code& ec;
parse_event& event;

template< class I >
bool operator()( I ) const
{
auto& handler = variant2::get<I::value + 1>(self->inner_);
using Handler = remove_cvref<decltype(handler)>;
return variant2::visit(
event_visitor<Handler>{handler, ec}, event );
}
};
bool process_events(error_code& ec)
{
constexpr std::size_t N = variant_size::value;

// should be pointers not iterators, otherwise MSVC crashes
auto const last = events_.data() + events_.size();
auto first = last - 1;
bool ok = false;

if( inner_active_ < 0 )
next_alternative();
do
{
if( static_cast<std::size_t>(inner_active_) >= N )
{
BOOST_JSON_FAIL( ec, error::exhausted_variants );
return false;
}

for ( ; first != last; ++first )
{
ok = mp11::mp_with_index< N >(
inner_active_, event_processor{this, ec, *first} );
if( !ok )
{
first = events_.data();
next_alternative();
ec.clear();
break;
}
}
}
while( !ok );

return true;
}

#define BOOST_JSON_INVOKE_INNER(ev, ec) \
events_.emplace_back( ev ); \
return process_events(ec);

bool on_object_begin( error_code& ec )
{
BOOST_JSON_INVOKE_INNER( object_begin_handler_event{}, ec );
}

bool on_object_end( error_code& ec, std::size_t )
{
BOOST_JSON_INVOKE_INNER( object_end_handler_event{}, ec );
}

bool on_array_begin( error_code& ec )
{
BOOST_JSON_INVOKE_INNER( array_begin_handler_event{}, ec );
}

bool on_array_end( error_code& ec, std::size_t )
{
if( !inner_active_ )
{
signal_end();
return true;
}

BOOST_JSON_INVOKE_INNER( array_end_handler_event{}, ec );
}

bool on_key_part( error_code&, string_view sv, std::size_t )
{
string_.append(sv);
return true;
}

bool on_key( error_code& ec, string_view sv, std::size_t )
{
string_.append(sv);
BOOST_JSON_INVOKE_INNER( key_handler_event{ std::move(string_) }, ec );
}

bool on_string_part( error_code&, string_view sv, std::size_t )
{
string_.append(sv);
return true;
}

bool on_string( error_code& ec, string_view sv, std::size_t )
{
string_.append(sv);
BOOST_JSON_INVOKE_INNER(
string_handler_event{ std::move(string_) }, ec );
}

bool on_number_part( error_code&, string_view )
{
return true;
}

bool on_int64( error_code& ec, std::int64_t v, string_view )
{
BOOST_JSON_INVOKE_INNER( int64_handler_event{v}, ec );
}

bool on_uint64( error_code& ec, std::uint64_t v, string_view )
{
BOOST_JSON_INVOKE_INNER( uint64_handler_event{v}, ec );
}

bool on_double( error_code& ec, double v, string_view )
{
BOOST_JSON_INVOKE_INNER( double_handler_event{v}, ec );
}

bool on_bool( error_code& ec, bool v )
{
BOOST_JSON_INVOKE_INNER( bool_handler_event{v}, ec );
}

bool on_null( error_code& ec )
{
BOOST_JSON_INVOKE_INNER( null_handler_event{}, ec );
}

#undef BOOST_JSON_INVOKE_INNER
};

// optional handler
template<class V, class P>
class converting_handler<optional_conversion_tag, V, P>
Expand Down
Loading