Skip to content

Commit bfedf3c

Browse files
committed
enable conversion for derived described structs
1 parent 6c79a2c commit bfedf3c

File tree

9 files changed

+151
-41
lines changed

9 files changed

+151
-41
lines changed

CHANGELOG.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55

66
include::doc/pages/definitions.adoc[]
77

8+
== Boost 1.89.0
9+
10+
.API Changes
11+
* {issue}1086[#1086] Described class support is enabled for types with bases.
12+
813
== Boost 1.87.0
914

1015
.API Changes
@@ -13,7 +18,6 @@ include::doc/pages/definitions.adoc[]
1318
* {issue}1040[#1040] Deprecated initilaizer list behavior was removed.
1419
* {issue}1040[#1040] Deprecated type aliases were removed.
1520

16-
1721
.New Features
1822
* {issue}956[#956] <<direct_conversion,Direct serialization>>.
1923
* {issue}644[#644] Add GDB pretty printers for Boost.JSON types.

include/boost/json/conversion.hpp

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -280,26 +280,29 @@ struct is_null_like
280280
/** Determine if `T` should be treated as a described class.
281281
282282
Described classes are serialised as objects with an element for each
283-
described data member. A described class should not have described bases or
284-
non-public members.
283+
described data member. Described bases are serialized in a flattened way,
284+
that is members of bases are serialized as direct elements of the object,
285+
and no nested objects are created for bases.
285286
286-
Or more formally, given `L`, a class template of the form
287-
`template<class...> struct L {};`, if
287+
A described class should not have non-public described members (including
288+
inherited members) or non-public non-empty described bases. Or more
289+
formally, given `L`, a class template of the form
290+
`template<class...> struct L {}`, if
288291
289-
- `boost::describe::has_members<T, boost::describe::mod_public>::value` is
290-
`true`; and
292+
- `boost::describe::has_describe_members<T>::value` is `true`; and
291293
292294
- `boost::describe::describe_members<T, boost::describe::mod_private |
293-
boost::describe::mod_protected>` denotes `L<>`; and
294-
295-
- `boost::describe::describe_bases<T, boost::describe::mod_any_access>`
296-
denotes `L<>`; and
295+
boost::describe::mod_protected | boost::describe::mod_inherited>` denotes
296+
`L<>`; and
297297
298298
- `std::is_union<T>::value` is `false`;
299299
300300
then the trait provides the member constant `value` that is equal to
301301
`true`. Otherwise, `value` is equal to `false`.
302302
303+
@note Shadowed members are ignored both for requirements checking and for
304+
performing conversions.
305+
303306
Users can specialize the trait for their own types if they don't want them
304307
to be treated as described classes. For example:
305308
@@ -316,10 +319,8 @@ struct is_null_like
316319
@endcode
317320
318321
Users can also specialize the trait for their own types _with_ described
319-
bases or described non-public data members to enable this conversion
320-
implementation. In this case the class will be serialized in a flattened
321-
way, that is members of bases will be serialized as direct elements of the
322-
object, and no nested objects will be created for bases.
322+
non-public data members to enable this conversion implementation. Note that
323+
non-public bases are not supported regardless.
323324
324325
@see [Boost.Describe](https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html).
325326
*/

include/boost/json/detail/parse_into.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,6 +1130,10 @@ class converting_handler<described_class_conversion_tag, V, P>
11301130
#else
11311131

11321132
private:
1133+
static_assert(
1134+
uniquely_named_members<V>::value,
1135+
"The type has several described members with the same name.");
1136+
11331137
using Dm = described_members<V>;
11341138
using Dt = struct_element_list<V>;
11351139

include/boost/json/detail/value_from.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ value_from_impl( no_conversion_tag, value&, T&&, Ctx const& )
155155
template< class Ctx, class T >
156156
struct from_described_member
157157
{
158+
static_assert(
159+
uniquely_named_members< remove_cvref<T> >::value,
160+
"The type has several described members with the same name.");
161+
158162
using Ds = described_members< remove_cvref<T> >;
159163

160164
object& obj;

include/boost/json/detail/value_to.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ value_to_impl(
349349
template< class Ctx, class T >
350350
struct to_described_member
351351
{
352+
static_assert(
353+
uniquely_named_members<T>::value,
354+
"The type has several described members with the same name.");
355+
352356
using Ds = described_members<T>;
353357

354358
system::result<T>& res;

include/boost/json/impl/conversion.hpp

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -268,10 +268,10 @@ using has_user_conversion3 = mp11::mp_if<
268268

269269
template< class T >
270270
using described_non_public_members = describe::describe_members<
271-
T, describe::mod_private | describe::mod_protected>;
272-
template< class T >
273-
using described_bases = describe::describe_bases<
274-
T, describe::mod_any_access>;
271+
T,
272+
describe::mod_private
273+
| describe::mod_protected
274+
| boost::describe::mod_inherited>;
275275

276276
#if defined(BOOST_MSVC) && BOOST_MSVC < 1920
277277

@@ -301,6 +301,45 @@ template< class T >
301301
using described_members = describe::describe_members<
302302
T, describe::mod_any_access | describe::mod_inherited>;
303303

304+
#ifdef BOOST_DESCRIBE_CXX14
305+
306+
constexpr
307+
bool
308+
compare_strings(char const* l, char const* r)
309+
{
310+
#if defined(_MSC_VER) && (_MSC_VER <= 1900) && !defined(__clang__)
311+
return *l == *r && ( (*l == 0) | compare_strings(l + 1, r + 1) );
312+
#else
313+
do
314+
{
315+
if( *l != *r )
316+
return false;
317+
if( *l == 0 )
318+
return true;
319+
++l;
320+
++r;
321+
} while(true);
322+
#endif
323+
}
324+
325+
template< class L, class R >
326+
struct equal_member_names
327+
: mp11::mp_bool< compare_strings(L::name, R::name) >
328+
{};
329+
330+
template< class T >
331+
using uniquely_named_members = mp11::mp_same<
332+
mp11::mp_unique_if< described_members<T>, equal_member_names >,
333+
described_members<T> >;
334+
335+
#else
336+
337+
// we only check this in C++14, but the template should exist nevertheless
338+
template< class T >
339+
using uniquely_named_members = std::true_type;
340+
341+
#endif // BOOST_DESCRIBE_CXX14
342+
304343
// user conversion (via tag_invoke)
305344
template< class Ctx, class T, class Dir >
306345
using user_conversion_category = mp11::mp_cond<
@@ -541,9 +580,7 @@ struct is_described_class
541580
mp11::mp_not< std::is_union<T> >,
542581
mp11::mp_empty<
543582
mp11::mp_eval_or<
544-
mp11::mp_list<>, detail::described_non_public_members, T>>,
545-
mp11::mp_empty<
546-
mp11::mp_eval_or<mp11::mp_list<>, detail::described_bases, T>>>
583+
mp11::mp_list<>, detail::described_non_public_members, T>>>
547584
{ };
548585

549586
template<class T>

include/boost/json/impl/serializer.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,10 @@ write_impl(tuple_conversion_tag, writer& w, stream& ss0)
463463
template< class T, bool StackEmpty >
464464
struct serialize_struct_elem_helper
465465
{
466+
static_assert(
467+
uniquely_named_members<T>::value,
468+
"The type has several described members with the same name.");
469+
466470
writer& w;
467471
local_stream& ss;
468472
T const* pt;

test/conversion.cpp

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,44 +73,62 @@ struct pseudo_multimap1
7373

7474
struct my_null { };
7575

76-
struct described1 { };
77-
BOOST_DESCRIBE_STRUCT(described1, (), ())
76+
struct described1 { int n1; };
77+
BOOST_DESCRIBE_STRUCT(described1, (), (n1))
7878

79-
struct described2 : described1 { };
80-
BOOST_DESCRIBE_STRUCT(described2, (described1), ())
79+
struct described2 : described1 { int n2; };
80+
BOOST_DESCRIBE_STRUCT(described2, (described1), (n2))
8181

82-
struct described3
82+
struct described3 : private described1 { int n3; };
83+
BOOST_DESCRIBE_STRUCT(described3, (described1), (n3))
84+
85+
struct described4 : protected described1 { };
86+
BOOST_DESCRIBE_STRUCT(described4, (described1), ())
87+
88+
struct described5 : private described2, protected described3 { };
89+
BOOST_DESCRIBE_STRUCT(described5, (), ())
90+
91+
struct described6
8392
{
8493
int n;
8594

8695
private:
8796
int m;
8897
};
89-
BOOST_DESCRIBE_STRUCT(described3, (), (n))
98+
BOOST_DESCRIBE_STRUCT(described6, (), (n))
9099

91-
struct described4
100+
struct described7
92101
{
93102
int n;
94103

95104
private:
96105
int m;
97-
BOOST_DESCRIBE_CLASS(described4, (), (n), (), (m))
106+
BOOST_DESCRIBE_CLASS(described7, (), (n), (), (m))
98107
};
99108

100-
struct described5
109+
struct described8
101110
{
102111
int n;
103112

104113
protected:
105114
int m;
106-
BOOST_DESCRIBE_CLASS(described5, (), (n), (m), ())
115+
BOOST_DESCRIBE_CLASS(described8, (), (n), (m), ())
107116
};
108117

109-
union described6
118+
union described9
110119
{
111120
int n;
112121
};
113-
BOOST_DESCRIBE_STRUCT(described6, (), (n))
122+
BOOST_DESCRIBE_STRUCT(described9, (), (n))
123+
124+
struct base1 {};
125+
BOOST_DESCRIBE_STRUCT(base1, (), ())
126+
127+
struct base2 : private base1 {};
128+
BOOST_DESCRIBE_STRUCT(base2, (base1), ())
129+
130+
struct described10 : base2 {};
131+
BOOST_DESCRIBE_STRUCT(described10, (base2), ())
114132

115133
enum class described_enum { e };
116134
BOOST_DESCRIBE_ENUM(described_enum, e)
@@ -182,13 +200,17 @@ class conversion_test
182200

183201
#ifdef BOOST_DESCRIBE_CXX14
184202
BOOST_STATIC_ASSERT( is_described_class<described1>::value );
185-
BOOST_STATIC_ASSERT( is_described_class<described3>::value );
203+
BOOST_STATIC_ASSERT( is_described_class<described2>::value );
204+
BOOST_STATIC_ASSERT( is_described_class<described5>::value );
205+
BOOST_STATIC_ASSERT( is_described_class<described6>::value );
186206

187207
BOOST_STATIC_ASSERT( !is_described_class<my_null>::value );
188-
BOOST_STATIC_ASSERT( !is_described_class<described2>::value );
208+
BOOST_STATIC_ASSERT( !is_described_class<described3>::value );
189209
BOOST_STATIC_ASSERT( !is_described_class<described4>::value );
190-
BOOST_STATIC_ASSERT( !is_described_class<described5>::value );
191-
BOOST_STATIC_ASSERT( !is_described_class<described6>::value );
210+
BOOST_STATIC_ASSERT( !is_described_class<described7>::value );
211+
BOOST_STATIC_ASSERT( !is_described_class<described8>::value );
212+
BOOST_STATIC_ASSERT( !is_described_class<described9>::value );
213+
// BOOST_STATIC_ASSERT( !is_described_class<described10>::value );
192214

193215
BOOST_STATIC_ASSERT( is_described_enum<described_enum>::value );
194216
BOOST_STATIC_ASSERT( !is_described_enum<my_null>::value );

test/value_from.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ struct T11 : T10
184184
{
185185
std::string s;
186186

187+
void
188+
set_n(int v)
189+
{
190+
n = v;
191+
}
192+
193+
void
194+
set_d(double v)
195+
{
196+
d = v;
197+
}
198+
187199
void
188200
set_b(bool v)
189201
{
@@ -196,6 +208,24 @@ struct T11 : T10
196208
BOOST_DESCRIBE_CLASS(T11, (T10), (s), (), (b))
197209
};
198210

211+
struct Base1
212+
{
213+
int a = 1;
214+
};
215+
BOOST_DESCRIBE_STRUCT(Base1, (), (a))
216+
217+
struct Base2
218+
{
219+
int a = 2;
220+
};
221+
BOOST_DESCRIBE_STRUCT(Base2, (), (a))
222+
223+
struct Derived: Base1, Base2
224+
{
225+
int a = 3;
226+
};
227+
BOOST_DESCRIBE_STRUCT(Derived, (Base1, Base2), ())
228+
199229
//----------------------------------------------------------
200230

201231
BOOST_DEFINE_ENUM_CLASS(E1, a, b, c)
@@ -495,10 +525,10 @@ class value_from_test
495525
BOOST_TEST(( jv == value{{"n", 909}, {"d", -1.45}} ));
496526

497527
::value_from_test_ns::T11 t11;
498-
t11.n = 67;
499-
t11.d = -.12;
500-
t11.s = "qwerty";
528+
t11.set_n(67);
529+
t11.set_d(-.12);
501530
t11.set_b(true);
531+
t11.s = "qwerty";
502532
jv = value_from( t11, ctx... );
503533
BOOST_TEST(( jv == value{{"n", 67}, {"d", -.12}, {"s", "qwerty"}, {"b", true}} ));
504534

0 commit comments

Comments
 (0)