Skip to content

Commit 3dec165

Browse files
committed
value_from supports conversion through helper types
1 parent baff1cb commit 3dec165

File tree

6 files changed

+159
-7
lines changed

6 files changed

+159
-7
lines changed

doc/Jamfile

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import boostbook ;
1414
import ../../../tools/docca/docca.jam ;
1515
import path ;
1616

17-
local include-prefix = [ path.root $(__file__:D) [ path.pwd ] ] ;
18-
include-prefix = [ path.native $(include-prefix:D)/include ] ;
17+
local include-prefix = [ path.join $(__file__:D) .. ] ;
18+
include-prefix = [ path.native $(include-prefix)/include ] ;
1919
docca.pyreference reference.qbk
2020
: [ glob-tree-ex ../include/boost/json : *.hpp *.ipp : detail impl ]
2121
externals.hpp

include/boost/json/conversion.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,15 @@ struct is_variant_like;
437437
template<class T>
438438
struct is_optional_like;
439439

440+
template< class T, class Context = void, class = void >
441+
struct represent_as
442+
{
443+
using type = T;
444+
};
445+
446+
template< class T, class Context = void >
447+
using represent_as_t = typename represent_as<T>::type;
448+
440449
} // namespace json
441450
} // namespace boost
442451

include/boost/json/detail/value_from.hpp

+42-1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,45 @@ struct append_tuple_element {
4141
}
4242
};
4343

44+
template< class T, class Rep, class = void >
45+
struct representation_helper
46+
{
47+
using result_to = Rep;
48+
49+
static
50+
result_to
51+
to( T&& t )
52+
{
53+
return static_cast<result_to>( static_cast<T&&>(t) );
54+
}
55+
};
56+
57+
template< class T, class Rep >
58+
struct representation_helper<
59+
T,
60+
Rep,
61+
typename std::enable_if<
62+
std::is_same<remove_cvref<T>, Rep>::value>::type >
63+
{
64+
using result_to = T&&;
65+
66+
static
67+
result_to
68+
to( T&& t )
69+
{
70+
return static_cast<T&&>(t);
71+
}
72+
};
73+
74+
template< class Ctx, class T >
75+
typename representation_helper<
76+
T, represent_as_t<remove_cvref<T>, Ctx> >::result_to
77+
to_representation( T&& t )
78+
{
79+
using Rep = represent_as_t<remove_cvref<T>, Ctx>;
80+
return representation_helper<T, Rep>::to( static_cast<T&&>(t) );
81+
}
82+
4483
//----------------------------------------------------------
4584
// User-provided conversion
4685

@@ -107,9 +146,11 @@ value_from_impl( map_like_conversion_tag, value& jv, T&& from, Ctx const& ctx )
107146
object& obj = jv.emplace_object();
108147
obj.reserve(detail::try_size(from, size_implementation<T>()));
109148
for (auto&& elem : from)
149+
{
110150
obj.emplace(
111-
get<0>(elem),
151+
to_representation<Ctx>( get<0>(elem) ),
112152
value_from( get<1>(elem), ctx, obj.storage() ));
153+
}
113154
}
114155

115156
// ranges

include/boost/json/impl/conversion.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,8 @@ struct is_map_like
516516
: mp11::mp_all<
517517
is_sequence_like<T>,
518518
mp11::mp_valid_and_true<detail::is_value_type_pair, T>,
519-
is_string_like<detail::key_type<T>>,
519+
is_string_like<
520+
represent_as_t<detail::remove_cvref< detail::key_type<T> >>>,
520521
mp11::mp_valid_and_true<detail::has_unique_keys, T>>
521522
{ };
522523

include/boost/json/value_from.hpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,15 @@ value_from(
8484
value& jv)
8585
{
8686
using bare_T = detail::remove_cvref<T>;
87+
using Rep = typename represent_as<bare_T>::type;
8788
BOOST_STATIC_ASSERT(detail::conversion_round_trips<
88-
Context, bare_T, detail::value_from_conversion>::value);
89-
using cat = detail::value_from_category<Context, bare_T>;
90-
detail::value_from_impl( cat(), jv, std::forward<T>(t), ctx );
89+
Context, Rep, detail::value_from_conversion>::value);
90+
using cat = detail::value_from_category<Context, Rep>;
91+
detail::value_from_impl(
92+
cat(),
93+
jv,
94+
detail::representation_helper<T, Rep>::to( static_cast<T&&>(t) ),
95+
ctx );
9196
}
9297

9398
/** Convert an object of type `T` to @ref value.

test/value_from.cpp

+96
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,60 @@ tag_invoke(
258258
}
259259

260260

261+
struct id
262+
{
263+
static constexpr auto& id1 = "Id#1";
264+
static constexpr auto& id2 = "Id#2";
265+
266+
std::size_t n;
267+
};
268+
269+
bool
270+
operator<(id l, id r) noexcept
271+
{
272+
return l.n < r.n;
273+
}
274+
275+
struct id_string_repr
276+
{
277+
std::size_t n;
278+
279+
id_string_repr(id x) noexcept
280+
: n(x.n)
281+
{}
282+
283+
id_string_repr(boost::json::string_view sv)
284+
{
285+
if( sv.data() == id::id1 )
286+
n = 1;
287+
else if( sv.data() == id::id2 )
288+
n = 2;
289+
else
290+
n = std::size_t(-1);
291+
}
292+
293+
operator id() const noexcept
294+
{
295+
return {n};
296+
}
297+
298+
operator boost::json::string_view() const noexcept
299+
{
300+
switch(n)
301+
{
302+
case 1: return boost::json::string_view(id::id1);
303+
case 2: return boost::json::string_view(id::id2);
304+
default: return boost::json::string_view("unknown");
305+
}
306+
}
307+
};
308+
309+
struct T14
310+
{
311+
id i;
312+
};
313+
BOOST_DESCRIBE_STRUCT(T14, (), (i))
314+
261315
} // namespace value_from_test_ns
262316

263317
template<class T>
@@ -296,6 +350,12 @@ struct is_described_class<::value_from_test_ns::T11>
296350
: std::true_type
297351
{ };
298352

353+
template<>
354+
struct represent_as<::value_from_test_ns::id>
355+
{
356+
using type = ::value_from_test_ns::id_string_repr;
357+
};
358+
299359
namespace {
300360

301361
template< class T, class... Context >
@@ -404,6 +464,23 @@ class value_from_test
404464
value b = value_from( a, ctx... );
405465
BOOST_TEST(b.is_null());
406466
}
467+
{
468+
value jv = value_from( value_from_test_ns::id{1}, ctx... );
469+
BOOST_TEST( jv == value("Id#1") );
470+
471+
jv = value_from( value_from_test_ns::id{2}, ctx... );
472+
BOOST_TEST( jv == value("Id#2") );
473+
474+
jv = value_from(
475+
std::vector<value_from_test_ns::id>{ {1}, {2}, {2}, {1} },
476+
ctx... );
477+
BOOST_TEST(( jv == value{"Id#1", "Id#2", "Id#2", "Id#1"} ));
478+
479+
jv = value_from(
480+
std::tuple<value_from_test_ns::id, int>{ {1}, 12 },
481+
ctx... );
482+
BOOST_TEST(( jv == value{"Id#1", 12} ));
483+
}
407484
}
408485

409486
template< class... Context >
@@ -447,6 +524,12 @@ class value_from_test
447524
BOOST_TEST(a.size() == c.as_array().size());
448525
BOOST_TEST(b.as_array().size() == c.as_array().size());
449526
}
527+
{
528+
value jv = value_from(
529+
std::map<value_from_test_ns::id, int>{ {{1}, 42}, {{2}, 43} },
530+
ctx... );
531+
BOOST_TEST(( jv == object{ {"Id#1", 42}, {"Id#2", 43} } ));
532+
}
450533
}
451534

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

591+
jv = value_from( value_from_test_ns::T14{ {1} }, ctx... );
592+
BOOST_TEST(( jv == object{ {"i", "Id#1"} } ));
593+
508594
e1 = ::value_from_test_ns::E1::b;
509595
BOOST_TEST( value_from( e1, ctx... ) == "b" );
510596

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

526612
BOOST_TEST( value_from( std::nullopt, ctx... ).is_null() );
613+
614+
jv = value_from(
615+
std::optional<value_from_test_ns::id>( {1} ), ctx... );
616+
BOOST_TEST( jv == value("Id#1") );
527617
#endif
528618
}
529619

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

639+
jv = value_from(
640+
std::variant<int, value_from_test_ns::id>(
641+
value_from_test_ns::id{2} ),
642+
ctx... );
643+
BOOST_TEST( jv == value("Id#2") );
644+
549645
BOOST_TEST( value() == value_from( std::monostate(), ctx... ) );
550646
#endif // BOOST_NO_CXX17_HDR_VARIANT
551647
}

0 commit comments

Comments
 (0)