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 Jul 17, 2024
1 parent baff1cb commit 3dec165
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 7 deletions.
4 changes: 2 additions & 2 deletions doc/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import boostbook ;
import ../../../tools/docca/docca.jam ;
import path ;

local include-prefix = [ path.root $(__file__:D) [ path.pwd ] ] ;
include-prefix = [ path.native $(include-prefix:D)/include ] ;
local include-prefix = [ path.join $(__file__:D) .. ] ;
include-prefix = [ path.native $(include-prefix)/include ] ;
docca.pyreference reference.qbk
: [ glob-tree-ex ../include/boost/json : *.hpp *.ipp : detail impl ]
externals.hpp
Expand Down
9 changes: 9 additions & 0 deletions include/boost/json/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,15 @@ struct is_variant_like;
template<class T>
struct is_optional_like;

template< class T, class Context = void, class = void >
struct represent_as
{
using type = T;
};

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

} // namespace json
} // namespace boost

Expand Down
43 changes: 42 additions & 1 deletion include/boost/json/detail/value_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,45 @@ struct append_tuple_element {
}
};

template< class T, class Rep, class = void >
struct representation_helper
{
using result_to = Rep;

static
result_to
to( T&& t )
{
return static_cast<result_to>( static_cast<T&&>(t) );
}
};

template< class T, class Rep >
struct representation_helper<
T,
Rep,
typename std::enable_if<
std::is_same<remove_cvref<T>, Rep>::value>::type >
{
using result_to = T&&;

static
result_to
to( T&& t )
{
return static_cast<T&&>(t);
}
};

template< class Ctx, class T >
typename representation_helper<
T, represent_as_t<remove_cvref<T>, Ctx> >::result_to
to_representation( T&& t )
{
using Rep = represent_as_t<remove_cvref<T>, Ctx>;
return representation_helper<T, Rep>::to( static_cast<T&&>(t) );
}

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

Expand Down Expand Up @@ -107,9 +146,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
3 changes: 2 additions & 1 deletion include/boost/json/impl/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,8 @@ 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<
represent_as_t<detail::remove_cvref< detail::key_type<T> >>>,
mp11::mp_valid_and_true<detail::has_unique_keys, T>>
{ };

Expand Down
11 changes: 8 additions & 3 deletions include/boost/json/value_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,15 @@ value_from(
value& jv)
{
using bare_T = detail::remove_cvref<T>;
using Rep = typename represent_as<bare_T>::type;
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, Rep, detail::value_from_conversion>::value);
using cat = detail::value_from_category<Context, Rep>;
detail::value_from_impl(
cat(),
jv,
detail::representation_helper<T, Rep>::to( static_cast<T&&>(t) ),
ctx );
}

/** Convert an object of type `T` to @ref value.
Expand Down
96 changes: 96 additions & 0 deletions test/value_from.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,60 @@ tag_invoke(
}


struct id
{
static constexpr auto& id1 = "Id#1";
static constexpr auto& id2 = "Id#2";

std::size_t n;
};

bool
operator<(id l, id r) noexcept
{
return l.n < r.n;
}

struct id_string_repr
{
std::size_t n;

id_string_repr(id x) noexcept
: n(x.n)
{}

id_string_repr(boost::json::string_view sv)
{
if( sv.data() == id::id1 )
n = 1;
else if( sv.data() == id::id2 )
n = 2;
else
n = std::size_t(-1);
}

operator id() const noexcept
{
return {n};
}

operator boost::json::string_view() const noexcept
{
switch(n)
{
case 1: return boost::json::string_view(id::id1);
case 2: return boost::json::string_view(id::id2);
default: return boost::json::string_view("unknown");
}
}
};

struct T14
{
id i;
};
BOOST_DESCRIBE_STRUCT(T14, (), (i))

} // namespace value_from_test_ns

template<class T>
Expand Down Expand Up @@ -296,6 +350,12 @@ struct is_described_class<::value_from_test_ns::T11>
: std::true_type
{ };

template<>
struct represent_as<::value_from_test_ns::id>
{
using type = ::value_from_test_ns::id_string_repr;
};

namespace {

template< class T, class... Context >
Expand Down Expand Up @@ -404,6 +464,23 @@ class value_from_test
value b = value_from( a, ctx... );
BOOST_TEST(b.is_null());
}
{
value jv = value_from( value_from_test_ns::id{1}, ctx... );
BOOST_TEST( jv == value("Id#1") );

jv = value_from( value_from_test_ns::id{2}, ctx... );
BOOST_TEST( jv == value("Id#2") );

jv = value_from(
std::vector<value_from_test_ns::id>{ {1}, {2}, {2}, {1} },
ctx... );
BOOST_TEST(( jv == value{"Id#1", "Id#2", "Id#2", "Id#1"} ));

jv = value_from(
std::tuple<value_from_test_ns::id, int>{ {1}, 12 },
ctx... );
BOOST_TEST(( jv == value{"Id#1", 12} ));
}
}

template< class... Context >
Expand Down Expand Up @@ -447,6 +524,12 @@ class value_from_test
BOOST_TEST(a.size() == c.as_array().size());
BOOST_TEST(b.as_array().size() == c.as_array().size());
}
{
value jv = value_from(
std::map<value_from_test_ns::id, int>{ {{1}, 42}, {{2}, 43} },
ctx... );
BOOST_TEST(( jv == object{ {"Id#1", 42}, {"Id#2", 43} } ));
}
}

template< class... Context >
Expand Down Expand Up @@ -505,6 +588,9 @@ class value_from_test
::value_from_test_ns::E1 e1 = ::value_from_test_ns::E1::a;
BOOST_TEST( value_from( e1, ctx... ) == "a" );

jv = value_from( value_from_test_ns::T14{ {1} }, ctx... );
BOOST_TEST(( jv == object{ {"i", "Id#1"} } ));

e1 = ::value_from_test_ns::E1::b;
BOOST_TEST( value_from( e1, ctx... ) == "b" );

Expand All @@ -524,6 +610,10 @@ class value_from_test
BOOST_TEST( jv == (value{1, 2, 3, nullptr, 5}) );

BOOST_TEST( value_from( std::nullopt, ctx... ).is_null() );

jv = value_from(
std::optional<value_from_test_ns::id>( {1} ), ctx... );
BOOST_TEST( jv == value("Id#1") );
#endif
}

Expand All @@ -546,6 +636,12 @@ class value_from_test
jv = value_from( v, ctx... );
BOOST_TEST(jv == "T5");

jv = value_from(
std::variant<int, value_from_test_ns::id>(
value_from_test_ns::id{2} ),
ctx... );
BOOST_TEST( jv == value("Id#2") );

BOOST_TEST( value() == value_from( std::monostate(), ctx... ) );
#endif // BOOST_NO_CXX17_HDR_VARIANT
}
Expand Down

0 comments on commit 3dec165

Please sign in to comment.