Skip to content

Commit

Permalink
allow extra members for converted structs
Browse files Browse the repository at this point in the history
  • Loading branch information
grisumbras committed Oct 16, 2024
1 parent 3804d73 commit 0534d31
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 70 deletions.
203 changes: 162 additions & 41 deletions include/boost/json/detail/parse_into.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -711,16 +711,16 @@ struct handler_tuple;

template< class P, template<class...> class L, class... V, std::size_t... I >
struct handler_tuple< P, L<V...>, mp11::index_sequence<I...> >
: handler_tuple_element< I, get_handler<V, P> >
: handler_tuple_element<I, V>
...
{
handler_tuple( handler_tuple const& ) = delete;
handler_tuple& operator=( handler_tuple const& ) = delete;

template< class Access, class T >
handler_tuple( Access access, T* pv, P* pp )
: handler_tuple_element< I, get_handler<V, P> >(
access( pv, mp11::mp_int<I>() ),
: handler_tuple_element<I, V>(
access( pv, mp11::mp_size_t<I>() ),
pp )
...
{ }
Expand Down Expand Up @@ -803,10 +803,17 @@ class converting_handler<tuple_conversion_tag, T, P>
{

private:
using ElementTypes = tuple_element_list<T>;

template<class V>
using ElementHandler = get_handler<V, converting_handler>;
using InnerHandlers = mp11::mp_transform<ElementHandler, ElementTypes>;
using HandlerTuple = handler_tuple<converting_handler, InnerHandlers>;

T* value_;
P* parent_;

handler_tuple< converting_handler, tuple_element_list<T> > handlers_;
HandlerTuple handlers_;
int inner_active_ = -1;

public:
Expand Down Expand Up @@ -874,7 +881,7 @@ class converting_handler<tuple_conversion_tag, T, P>

struct do_on_array_begin
{
handler_tuple< converting_handler, tuple_element_list<T> >& handlers;
HandlerTuple& handlers;
system::error_code& ec;

template< class I >
Expand Down Expand Up @@ -905,7 +912,7 @@ class converting_handler<tuple_conversion_tag, T, P>

struct do_on_array_end
{
handler_tuple< converting_handler, tuple_element_list<T> >& handlers;
HandlerTuple& handlers;
system::error_code& ec;

template< class I >
Expand Down Expand Up @@ -966,6 +973,13 @@ using struct_element_list = mp11::mp_transform_q<

struct struct_accessor
{
template< class T >
auto operator()( T* t, mp11::mp_size< described_members<T> > ) const
-> void*
{
return nullptr;
}

template< class T, class I >
auto operator()( T* t, I ) const
-> described_member_t<T, mp11::mp_at< described_members<T>, I> >*
Expand All @@ -976,16 +990,128 @@ struct struct_accessor
}
};

template< class F >
struct struct_key_searcher
{
F fn;
string_view key;
int& found;
int index = 0;

template< class D >
void
operator()( D ) const
operator()( D )
{
if( key == D::name )
found = index;
++index;
}
};

template<class P>
struct ignoring_handler
{
P* parent_;
std::size_t array_depth_ = 0;
std::size_t object_depth_ = 0;

ignoring_handler(ignoring_handler const&) = delete;
ignoring_handler& operator=(ignoring_handler const&) = delete;

ignoring_handler(void*, P* p) noexcept
: parent_(p)
{}

bool on_object_begin(system::error_code&)
{
++object_depth_;
return true;
}

bool on_object_end(system::error_code& ec)
{
BOOST_ASSERT( object_depth_ > 0 );
--object_depth_;

if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_array_begin(system::error_code&)
{
++array_depth_;
return true;
}

bool on_array_end(system::error_code& ec)
{
BOOST_ASSERT( array_depth_ > 0 );
--array_depth_;

if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_end(ec);
return true;
}

bool on_key_part(system::error_code&, string_view)
{
return true;
}

bool on_key(system::error_code&, string_view)
{
return true;
}

bool on_string_part(system::error_code&, string_view)
{
return true;
}

bool on_string(system::error_code& ec, string_view)
{
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_number_part(system::error_code&)
{
return true;
}

bool on_int64(system::error_code& ec, std::int64_t)
{
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_uint64(system::error_code& ec, std::uint64_t)
{
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_double(system::error_code& ec, double)
{
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_bool(system::error_code& ec, bool)
{
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}

bool on_null(system::error_code& ec)
{
fn( D::name ) ;
if( (array_depth_ + object_depth_) == 0 )
return parent_->signal_value(ec);
return true;
}
};

Expand All @@ -1000,14 +1126,22 @@ class converting_handler<described_class_conversion_tag, V, P>
#else

private:
using Dm = described_members<V>;
using Dt = struct_element_list<V>;

template<class T>
using MemberHandler = get_handler<T, converting_handler>;
using InnerHandlers = mp11::mp_push_back<
mp11::mp_transform<MemberHandler, Dt>,
ignoring_handler<converting_handler> >;
using InnerCount = mp11::mp_size<InnerHandlers>;

V* value_;
P* parent_;

std::string key_;

using Dm = described_members<V>;

handler_tuple< converting_handler, struct_element_list<V> > handlers_;
handler_tuple<converting_handler, InnerHandlers> handlers_;
int inner_active_ = -1;
std::size_t activated_ = 0;

Expand All @@ -1019,23 +1153,27 @@ class converting_handler<described_class_conversion_tag, V, P>
: value_(v), parent_(p), handlers_(struct_accessor(), v, this)
{}

struct is_optional_checker
struct is_required_checker
{
bool operator()( mp11::mp_size<Dt> ) const noexcept
{
return false;
}

template< class I >
bool operator()( I ) const noexcept
auto operator()( I ) const noexcept
{
using L = struct_element_list<V>;
using T = mp11::mp_at<L, I>;
using T = mp11::mp_at<Dt, I>;
return !is_optional_like<T>::value;
}
};

bool signal_value(system::error_code&)
{
BOOST_ASSERT( inner_active_ >= 0 );
bool required_member = mp11::mp_with_index< mp11::mp_size<Dm> >(
bool required_member = mp11::mp_with_index<InnerCount>(
inner_active_,
is_optional_checker{});
is_required_checker{});
if( required_member )
++activated_;

Expand All @@ -1060,7 +1198,7 @@ class converting_handler<described_class_conversion_tag, V, P>
auto f = [&](auto& handler) { return handler.fn ; }; \
using F = decltype(f); \
using H = decltype(handlers_); \
return mp11::mp_with_index< mp11::mp_size<Dm> >( \
return mp11::mp_with_index<InnerCount>( \
inner_active_, \
tuple_handler_op_invoker<H, F>{handlers_, f} );

Expand All @@ -1076,9 +1214,8 @@ class converting_handler<described_class_conversion_tag, V, P>
{
if( inner_active_ < 0 )
{
using L = struct_element_list<V>;
using C = mp11::mp_count_if<L, is_optional_like>;
constexpr int N = mp11::mp_size<L>::value - C::value;
using C = mp11::mp_count_if<Dt, is_optional_like>;
constexpr int N = mp11::mp_size<Dt>::value - C::value;
if( activated_ < N )
{
BOOST_JSON_FAIL( ec, error::size_mismatch );
Expand Down Expand Up @@ -1129,24 +1266,8 @@ class converting_handler<described_class_conversion_tag, V, P>
key = key_;
}

int i = 0;

auto f = [&](char const* name)
{
if( key == name )
inner_active_ = i;
++i;
};

mp11::mp_for_each<Dm>(
struct_key_searcher<decltype(f)>{f} );

if( inner_active_ < 0 )
{
BOOST_JSON_FAIL(ec, error::unknown_name);
return false;
}

inner_active_ = InnerCount::value - 1;
mp11::mp_for_each<Dm>( struct_key_searcher{key, inner_active_} );
return true;
}

Expand Down
23 changes: 4 additions & 19 deletions include/boost/json/detail/value_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,13 @@ value_to_impl(
*arr, ctx, boost::mp11::make_index_sequence<N>());
}

template< class Ctx, class T, bool non_throwing = true >
template< class Ctx, class T >
struct to_described_member
{
using Ds = described_members<T>;

using result_type = mp11::mp_eval_if_c<
!non_throwing, T, system::result, T>;

result_type& res;
system::result<T>& res;
object const& obj;
std::size_t count;
Ctx const& ctx;

template< class I >
Expand All @@ -375,7 +371,7 @@ struct to_described_member
BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
{
system::error_code ec;
BOOST_JSON_FAIL(ec, error::unknown_name);
BOOST_JSON_FAIL(ec, error::size_mismatch);
res = {boost::system::in_place_error, ec};
}
return;
Expand All @@ -391,10 +387,7 @@ struct to_described_member
# pragma GCC diagnostic pop
#endif
if( member_res )
{
(*res).* D::pointer = std::move(*member_res);
++count;
}
else
res = {boost::system::in_place_error, member_res.error()};
}
Expand All @@ -421,7 +414,7 @@ value_to_impl(
return res;
}

to_described_member< Ctx, T > member_converter{ res, *obj, 0u, ctx };
to_described_member<Ctx, T> member_converter{res, *obj, ctx};

using Ds = typename decltype(member_converter)::Ds;
constexpr std::size_t N = mp11::mp_size<Ds>::value;
Expand All @@ -430,14 +423,6 @@ value_to_impl(
if( !res )
return res;

if( member_converter.count != obj->size() )
{
system::error_code ec;
BOOST_JSON_FAIL(ec, error::size_mismatch);
res = {boost::system::in_place_error, ec};
return res;
}

return res;
}

Expand Down
Loading

0 comments on commit 0534d31

Please sign in to comment.