Skip to content

Commit 9e403f2

Browse files
committed
(try_)value_to supports conversion through helper types
1 parent 31154de commit 9e403f2

File tree

3 files changed

+166
-14
lines changed

3 files changed

+166
-14
lines changed

include/boost/json/detail/value_to.hpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ namespace json {
2626

2727
namespace detail {
2828

29+
template< class Ctx, class T >
30+
using value_to_attrs = conversion_attrs<
31+
Ctx, remove_cvref<T>, value_to_conversion>;
32+
2933
template<class T>
3034
using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
3135
template<class T>
@@ -232,11 +236,13 @@ value_to_impl(
232236
auto ins = detail::inserter(res, inserter_implementation<T>());
233237
for( key_value_pair const& kv: *obj )
234238
{
239+
using A = value_to_attrs< Ctx, key_type<T> >;
240+
using K = typename A::representation;
235241
auto elem_res = try_value_to<mapped_type<T>>( kv.value(), ctx );
236242
if( elem_res.has_error() )
237243
return {boost::system::in_place_error, elem_res.error()};
238244
*ins++ = value_type<T>{
239-
key_type<T>(kv.key()),
245+
K(kv.key()),
240246
std::move(*elem_res)};
241247
}
242248
return res;
@@ -817,10 +823,6 @@ value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
817823
return value_to_impl(impl, try_value_to_tag<T>(), jv, ctx).value();
818824
}
819825

820-
template< class Ctx, class T >
821-
using value_to_attrs = conversion_attrs<
822-
Ctx, remove_cvref<T>, value_to_conversion>;
823-
824826
} // detail
825827

826828
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL

include/boost/json/value_to.hpp

+8-9
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,13 @@ value_to( value const& jv, Context const& ctx )
9191
BOOST_STATIC_ASSERT(! std::is_reference<T>::value);
9292

9393
using Attrs = detail::value_to_attrs<Context, T>;
94+
using Rep = typename Attrs::representation;
9495
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
95-
Context,
96-
typename Attrs::representation,
97-
detail::value_to_conversion>::value);
96+
Context, Rep, detail::value_to_conversion>::value);
9897

9998
using bare_T = detail::remove_cvref<T>;
100-
return detail::value_to_impl(
101-
typename Attrs::category(), value_to_tag<bare_T>(), jv, ctx);
99+
return static_cast<bare_T>(detail::value_to_impl(
100+
typename Attrs::category(), value_to_tag<Rep>(), jv, ctx ));
102101
}
103102

104103
/** Convert a @ref value to an object of type `T`.
@@ -230,14 +229,14 @@ try_value_to( value const& jv, Context const& ctx )
230229
BOOST_STATIC_ASSERT(! std::is_reference<T>::value);
231230

232231
using Attrs = detail::value_to_attrs<Context, T>;
232+
using Rep = typename Attrs::representation;
233233
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
234-
Context,
235-
typename Attrs::representation,
236-
detail::value_to_conversion>::value);
234+
Context, Rep, detail::value_to_conversion>::value);
237235

238236
using bare_T = detail::remove_cvref<T>;
239237
return detail::value_to_impl(
240-
typename Attrs::category(), try_value_to_tag<bare_T>(), jv, ctx );
238+
typename Attrs::category(), try_value_to_tag<Rep>(), jv, ctx )
239+
& [](Rep&& rep) { return static_cast<bare_T>(rep); };
241240
}
242241

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

test/value_to.cpp

+151
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,97 @@ tag_invoke(
196196
return T11( jv.to_number<int>() );
197197
}
198198

199+
struct id
200+
{
201+
static constexpr auto& id1 = "Id#1";
202+
static constexpr auto& id2 = "Id#2";
203+
204+
std::size_t n;
205+
};
206+
207+
bool
208+
operator==(id l, id r) noexcept
209+
{
210+
return l.n == r.n;
211+
}
212+
213+
bool
214+
operator!=(id l, id r) noexcept
215+
{
216+
return l.n != r.n;
217+
}
218+
219+
bool
220+
operator<(id l, id r) noexcept
221+
{
222+
return l.n < r.n;
223+
}
224+
225+
struct id_string_repr
226+
{
227+
std::size_t n;
228+
229+
id_string_repr(id x) noexcept
230+
: n(x.n)
231+
{}
232+
233+
id_string_repr(boost::json::string_view sv)
234+
{
235+
if( std::equal( sv.begin(), sv.end(), id::id1) )
236+
n = 1;
237+
else if( std::equal( sv.begin(), sv.end(), id::id2) )
238+
n = 2;
239+
else
240+
throw std::runtime_error( "unknown id" );
241+
}
242+
243+
operator id() const noexcept
244+
{
245+
return {n};
246+
}
247+
248+
operator boost::json::string_view() const noexcept
249+
{
250+
switch(n)
251+
{
252+
case 1: return boost::json::string_view(id::id1);
253+
case 2: return boost::json::string_view(id::id2);
254+
default: return boost::json::string_view("unknown");
255+
}
256+
}
257+
};
258+
259+
struct T12
260+
{
261+
id i;
262+
};
263+
BOOST_DESCRIBE_STRUCT(T12, (), (i))
264+
265+
struct as_string {};
266+
267+
struct int_as_string
268+
{
269+
std::string s;
270+
271+
int_as_string(int n) noexcept
272+
: s( std::to_string(n) )
273+
{}
274+
275+
int_as_string(boost::json::string_view sv)
276+
: s(sv)
277+
{}
278+
279+
operator int() const noexcept
280+
{
281+
return std::atoi( s.data() );
282+
}
283+
284+
operator boost::json::string_view() const noexcept
285+
{
286+
return s;
287+
}
288+
};
289+
199290
} // namespace value_to_test_ns
200291

201292
namespace std
@@ -225,6 +316,18 @@ struct is_null_like<::value_to_test_ns::T1> : std::true_type { };
225316
template<>
226317
struct is_described_class<::value_to_test_ns::T7> : std::true_type { };
227318

319+
template<>
320+
struct represent_as<::value_to_test_ns::id>
321+
{
322+
using type = ::value_to_test_ns::id_string_repr;
323+
};
324+
325+
template <>
326+
struct represent_as<int, value_to_test_ns::as_string>
327+
{
328+
using type = ::value_to_test_ns::int_as_string;
329+
};
330+
228331
template <class T, class = void>
229332
struct can_apply_value_to
230333
: std::false_type
@@ -335,6 +438,19 @@ class value_to_test
335438
BOOST_TEST_CONV( arr, ctx... );
336439
}
337440

441+
BOOST_TEST((
442+
value_to< std::vector<value_to_test_ns::id> >(
443+
value{"Id#1", "Id#2", "Id#2", "Id#1"}, ctx... )
444+
== std::vector<value_to_test_ns::id>{{1}, {2}, {2}, {1}} ));
445+
BOOST_TEST((
446+
value_to< std::tuple<value_to_test_ns::id, int> >(
447+
value{"Id#1", 12}, ctx... )
448+
== std::tuple<value_to_test_ns::id, int>{{1}, 12} ));
449+
BOOST_TEST((
450+
value_to< std::map<value_to_test_ns::id, int> >(
451+
value{ {"Id#1", 42}, {"Id#2", 43} }, ctx... )
452+
== std::map<value_to_test_ns::id, int>{ {{1}, 42}, {{2}, 43} } ));
453+
338454
// mismatched type
339455
BOOST_TEST_THROWS_WITH_LOCATION(
340456
value_to<std::string>( value(), ctx... ));
@@ -464,6 +580,11 @@ class value_to_test
464580
BOOST_TEST( e1 == ::value_to_test_ns::E1::b );
465581
}
466582

583+
BOOST_TEST((
584+
value_to<value_to_test_ns::T12>(
585+
value(object{ {"i", "Id#1"} }), ctx... ).i
586+
== value_to_test_ns::T12{ {1} }.i ));
587+
467588
BOOST_TEST_THROWS_WITH_LOCATION(
468589
value_to<::value_to_test_ns::E1>( value(1), ctx... ));
469590
BOOST_TEST_THROWS_WITH_LOCATION(
@@ -506,6 +627,12 @@ class value_to_test
506627
value_to< std::nullopt_t >(value());
507628
BOOST_TEST_THROWS_WITH_LOCATION(
508629
value_to< std::nullopt_t >( jv, ctx... ));
630+
631+
BOOST_TEST((
632+
value_to< std::optional<value_to_test_ns::id> >(
633+
value("Id#1"), ctx... )
634+
== std::optional<value_to_test_ns::id>(
635+
value_to_test_ns::id{1} ) ));
509636
#endif
510637
}
511638

@@ -550,6 +677,10 @@ class value_to_test
550677
using V_T3_T1 = Variant<value_to_test_ns::T3, value_to_test_ns::T1>;
551678
auto v_t3_t1 = value_to<V_T3_T1>( jv, ctx... );
552679
BOOST_TEST( v_t3_t1.index() == 1 );
680+
681+
BOOST_TEST((
682+
value_to< Variant<value_to_test_ns::id> >( value("Id#2"), ctx... )
683+
== Variant<value_to_test_ns::id>( value_to_test_ns::id{2} )));
553684
}
554685

555686
template< class... Context >
@@ -757,6 +888,13 @@ class value_to_test
757888
testUserConversion( Context const& ... ctx )
758889
{
759890
value_to<value_to_test_ns::T2>( value("T2"), ctx... );
891+
892+
auto id = value_to<value_to_test_ns::id>(
893+
value("Id#1"), ctx... );
894+
BOOST_TEST( id.n == 1 );
895+
id = value_to<value_to_test_ns::id>(
896+
value("Id#2"), ctx... );
897+
BOOST_TEST( id.n == 2 );
760898
}
761899

762900
void
@@ -769,6 +907,19 @@ class value_to_test
769907
value_to<value_to_test_ns::T9>(
770908
value(), value_to_test_ns::custom_context() ),
771909
system::system_error);
910+
911+
BOOST_TEST((
912+
value_to< std::map<int, int> >(
913+
value{ {"1", "2"}, {"2", "4"}, {"3", "8"} },
914+
value_to_test_ns::as_string() )
915+
== std::map<int, int>{ {1,2}, {2,4}, {3,8} } ));
916+
BOOST_TEST((
917+
value_to< std::map<int, int> >(
918+
value{ {"1", "2"}, {"2", "4"}, {"3", "6"} },
919+
std::make_tuple(
920+
value_to_test_ns::as_string(),
921+
value_to_test_ns::custom_context() ))
922+
== std::map<int, int>{ {1,2}, {2,4}, {3,6} } ));
772923
}
773924

774925
struct run_templated_tests

0 commit comments

Comments
 (0)