Skip to content

Commit

Permalink
value_from supports conversion through helper types
Browse files Browse the repository at this point in the history
  • Loading branch information
grisumbras committed Jan 14, 2025
1 parent 57d42f0 commit 43cbac6
Show file tree
Hide file tree
Showing 10 changed files with 283 additions and 52 deletions.
8 changes: 7 additions & 1 deletion include/boost/json/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ struct is_sequence_like;
@see @ref value_from, @ref value_to
*/
template<class T>
template<class T, class Context>
struct is_map_like;

/** Determine if `T` can be treated like a tuple during conversions.
Expand Down Expand Up @@ -437,6 +437,12 @@ struct is_variant_like;
template<class T>
struct is_optional_like;

template< class T, class Context = void >
struct represent_as;

template< class T, class Context = void >
using represent_as_t = typename represent_as<T, Context>::type;

} // namespace json
} // namespace boost

Expand Down
3 changes: 2 additions & 1 deletion include/boost/json/detail/parse_into.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ class converting_handler;

// get_handler
template< class V, class P >
using get_handler = converting_handler< generic_conversion_category<V>, V, P >;
using get_handler = converting_handler<
generic_conversion_category<V, no_context>, V, P>;

template<error E> class handler_error_base
{
Expand Down
31 changes: 22 additions & 9 deletions include/boost/json/detail/value_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@

namespace boost {
namespace json {

namespace detail {

template< class Ctx, class T >
using value_from_attrs = conversion_attrs<
Ctx, remove_cvref<T>, value_from_conversion>;

template< class Ctx, class T >
struct append_tuple_element {
array& arr;
Expand All @@ -41,6 +44,21 @@ struct append_tuple_element {
}
};

template< class T, class Ctx >
using to_representation_result = mp11::mp_if<
std::is_same<
remove_cvref<T>, typename value_from_attrs<Ctx, T>::representation >,
T&&,
typename value_from_attrs<Ctx, T>::representation>;

template< class Ctx, class T >
to_representation_result< T, Ctx >
to_representation( T&& t )
{
using R = to_representation_result< T, Ctx >;
return static_cast<R>( static_cast<T&&>(t) );
}

//----------------------------------------------------------
// User-provided conversion

Expand Down Expand Up @@ -107,9 +125,11 @@ value_from_impl( map_like_conversion_tag, value& jv, T&& from, Ctx const& ctx )
object& obj = jv.emplace_object();
obj.reserve(detail::try_size(from, size_implementation<T>()));
for (auto&& elem : from)
{
obj.emplace(
get<0>(elem),
to_representation<Ctx>( get<0>(elem) ),
value_from( get<1>(elem), ctx, obj.storage() ));
}
}

// ranges
Expand Down Expand Up @@ -257,13 +277,6 @@ value_from_impl( path_conversion_tag, value& jv, T&& from, Ctx const& )
jv.emplace_string().assign(sv);
}

//----------------------------------------------------------
// Contextual conversions

template< class Ctx, class T >
using value_from_category = conversion_category<
Ctx, T, value_from_conversion >;

} // detail

#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
Expand Down
4 changes: 2 additions & 2 deletions include/boost/json/detail/value_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,8 +818,8 @@ value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
}

template< class Ctx, class T >
using value_to_category = conversion_category<
Ctx, T, value_to_conversion >;
using value_to_attrs = conversion_attrs<
Ctx, remove_cvref<T>, value_to_conversion>;

} // detail

Expand Down
2 changes: 1 addition & 1 deletion include/boost/json/fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct is_string_like;
template<class T>
struct is_sequence_like;

template<class T>
template<class T, class Context = void>
struct is_map_like;

template<class T>
Expand Down
106 changes: 81 additions & 25 deletions include/boost/json/impl/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ namespace boost {
namespace json {
namespace detail {

struct no_context
{};

#ifdef __cpp_lib_nonmember_container_access
using std::size;
#endif
Expand Down Expand Up @@ -317,14 +320,14 @@ using native_conversion_category = mp11::mp_cond<
std::is_same<T, string>, string_conversion_tag>;

// generic conversions
template< class T >
template< class T, class Ctx >
using generic_conversion_category = mp11::mp_cond<
std::is_same<T, bool>, bool_conversion_tag,
std::is_integral<T>, integral_conversion_tag,
std::is_floating_point<T>, floating_point_conversion_tag,
is_null_like<T>, null_like_conversion_tag,
is_string_like<T>, string_like_conversion_tag,
is_map_like<T>, map_like_conversion_tag,
is_map_like<T, Ctx>, map_like_conversion_tag,
is_sequence_like<T>, sequence_conversion_tag,
is_tuple_like<T>, tuple_conversion_tag,
is_described_class<T>, described_class_conversion_tag,
Expand All @@ -338,38 +341,92 @@ using generic_conversion_category = mp11::mp_cond<
template< class T >
using nested_type = typename T::type;
template< class T1, class T2 >
using conversion_category_impl_helper = mp11::mp_eval_if_not<
using conversion_category_helper = mp11::mp_eval_if_not<
std::is_same<detail::no_conversion_tag, T1>,
T1,
mp11::mp_eval_or_q, T1, mp11::mp_quote<nested_type>, T2>;

template< class Ctx >
using fix_context = mp11::mp_if< std::is_same<Ctx, no_context>, void, Ctx>;

template<class T, class Ctx>
using representation_or_void = mp11::mp_eval_or<void, represent_as_t, T, Ctx>;

template< class U >
using is_not_void = mp11::mp_bool< !std::is_same<void, U>::value >;

template< class T, class Ctxs >
struct representation_helper
{
using size = mp11::mp_size<Ctxs>;

template< class I >
using exists = mp11::mp_less<I, size>;

template< class Ctx >
using step = representation_or_void<T, Ctx>;
using reps = mp11::mp_transform<step, Ctxs>;
using r_index = mp11::mp_find_if< reps, is_not_void >;

using type = mp11::mp_eval_if<
mp11::mp_not< exists<r_index> >,
T,
mp11::mp_at, reps, r_index>;
};

template< class T, class Ctx >
struct conversion_representation_impl
: representation_helper< T, mp11::mp_list<Ctx, void> >
{};

template< class T >
struct conversion_representation_impl<T, no_context>
: representation_helper< T, mp11::mp_list<void> >
{};

template< class T, class... Ctxs >
struct conversion_representation_impl< T, std::tuple<Ctxs...> >
: representation_helper< T, mp11::mp_list<remove_cvref<Ctxs>..., void> >
{};

template< class T, class Ctx >
using conversion_representation
= typename conversion_representation_impl<T, Ctx>::type;

template< class Ctx, class T, class Dir >
struct conversion_category_impl
struct conversion_attrs
{
using type = mp11::mp_fold<
using representation = conversion_representation<T, Ctx>;

using category = mp11::mp_fold<
mp11::mp_list<
mp11::mp_defer<user_conversion_category, Ctx, T, Dir>,
mp11::mp_defer<native_conversion_category, T>,
mp11::mp_defer<generic_conversion_category, T>>,
mp11::mp_defer<user_conversion_category, Ctx, representation, Dir>,
mp11::mp_defer<native_conversion_category, representation>,
mp11::mp_defer<generic_conversion_category, representation, Ctx>>,
no_conversion_tag,
conversion_category_impl_helper>;
conversion_category_helper>;
};
template< class Ctx, class T, class Dir >
using conversion_category =
typename conversion_category_impl< Ctx, T, Dir >::type;

template< class T >
using any_conversion_tag = mp11::mp_not<
std::is_same< T, no_conversion_tag > >;

template< class Ctx, class T, class Dir >
using conversion_category = typename conversion_attrs<Ctx, T, Dir>::category;

template< class T, class Dir, class... Ctxs >
struct conversion_category_impl< std::tuple<Ctxs...>, T, Dir >
struct conversion_attrs< std::tuple<Ctxs...>, T, Dir >
{
using size = mp11::mp_size_t< sizeof...(Ctxs) >;
using ctxs = mp11::mp_list< remove_cvref<Ctxs>... >;
using cats = mp11::mp_list<
conversion_category<remove_cvref<Ctxs>, T, Dir>... >;

template< class I >
using exists = mp11::mp_less< I, mp11::mp_size<cats> >;
using exists = mp11::mp_less<I, size>;

using representation = conversion_representation< T, std::tuple<Ctxs...> >;

using cats = mp11::mp_list<
conversion_category<remove_cvref<Ctxs>, representation, Dir>... >;

using context2 = mp11::mp_find< cats, full_context_conversion_tag >;
using context1 = mp11::mp_find< cats, context_conversion_tag >;
Expand All @@ -379,14 +436,11 @@ struct conversion_category_impl< std::tuple<Ctxs...>, T, Dir >
exists<context1>, context1,
exists<context0>, context0,
mp11::mp_true, mp11::mp_find_if< cats, any_conversion_tag > >;
using type = mp11::mp_eval_or<
using category = mp11::mp_eval_or<
no_conversion_tag,
mp11::mp_at, cats, index >;
};

struct no_context
{};

template <class T, class Dir>
using can_convert = mp11::mp_not<
std::is_same<
Expand Down Expand Up @@ -456,10 +510,10 @@ template< class T, class Dir, class... Ctxs >
struct supported_context< std::tuple<Ctxs...>, T, Dir >
{
using Ctx = std::tuple<Ctxs...>;
using impl = conversion_category_impl<Ctx, T, Dir>;
using index = typename impl::index;
using Attrs = conversion_attrs<Ctx, T, Dir>;
using index = typename Attrs::index;
using next_supported = supported_context<
mp11::mp_at< typename impl::ctxs, index >, T, Dir >;
mp11::mp_at< typename Attrs::ctxs, index >, T, Dir >;
using type = typename next_supported::type;

static
Expand Down Expand Up @@ -508,12 +562,14 @@ struct is_sequence_like
mp11::mp_valid<detail::begin_iterator_category, T>>
{ };

template<class T>
template<class T, class Ctx>
struct is_map_like
: mp11::mp_all<
is_sequence_like<T>,
mp11::mp_valid_and_true<detail::is_value_type_pair, T>,
is_string_like<detail::key_type<T>>,
is_string_like<
detail::conversion_representation<
detail::remove_cvref< detail::key_type<T> >, Ctx>>,
mp11::mp_valid_and_true<detail::has_unique_keys, T>>
{ };

Expand Down
2 changes: 1 addition & 1 deletion include/boost/json/impl/serializer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ template<class T, bool StackEmpty>
bool
write_impl(writer& w, stream& ss)
{
using cat = detail::generic_conversion_category<T>;
using cat = detail::generic_conversion_category<T, detail::no_context>;
return write_impl<T, StackEmpty>( cat(), w, ss );
}

Expand Down
14 changes: 10 additions & 4 deletions include/boost/json/value_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,17 @@ value_from(
Context const& ctx,
value& jv)
{
using bare_T = detail::remove_cvref<T>;
using Attrs = detail::value_from_attrs<Context, T>;
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
Context, bare_T, detail::value_from_conversion>::value);
using cat = detail::value_from_category<Context, bare_T>;
detail::value_from_impl( cat(), jv, std::forward<T>(t), ctx );
Context,
typename Attrs::representation,
detail::value_from_conversion>::value);

detail::value_from_impl(
typename Attrs::category(),
jv,
detail::to_representation<Context>( static_cast<T&&>(t) ),
ctx );
}

/** Convert an object of type `T` to @ref value.
Expand Down
25 changes: 17 additions & 8 deletions include/boost/json/value_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,16 @@ T
value_to( value const& jv, Context const& ctx )
{
BOOST_STATIC_ASSERT(! std::is_reference<T>::value);
using bare_T = detail::remove_cvref<T>;

using Attrs = detail::value_to_attrs<Context, T>;
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
Context, bare_T, detail::value_to_conversion>::value);
using cat = detail::value_to_category<Context, bare_T>;
return detail::value_to_impl( cat(), value_to_tag<bare_T>(), jv, ctx );
Context,
typename Attrs::representation,
detail::value_to_conversion>::value);

using bare_T = detail::remove_cvref<T>;
return detail::value_to_impl(
typename Attrs::category(), value_to_tag<bare_T>(), jv, ctx);
}

/** Convert a @ref value to an object of type `T`.
Expand Down Expand Up @@ -223,12 +228,16 @@ typename result_for<T, value>::type
try_value_to( value const& jv, Context const& ctx )
{
BOOST_STATIC_ASSERT(! std::is_reference<T>::value);
using bare_T = detail::remove_cvref<T>;

using Attrs = detail::value_to_attrs<Context, T>;
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
Context, bare_T, detail::value_to_conversion>::value);
using cat = detail::value_to_category<Context, bare_T>;
Context,
typename Attrs::representation,
detail::value_to_conversion>::value);

using bare_T = detail::remove_cvref<T>;
return detail::value_to_impl(
cat(), try_value_to_tag<bare_T>(), jv, ctx );
typename Attrs::category(), try_value_to_tag<bare_T>(), jv, ctx );
}

/** Convert a @ref value to a `boost::system::result<T>`.
Expand Down
Loading

0 comments on commit 43cbac6

Please sign in to comment.