Skip to content

Commit 770e314

Browse files
committed
Add circumvent for zero-variadic macros - solve #43
1 parent b64177c commit 770e314

File tree

6 files changed

+123
-5
lines changed

6 files changed

+123
-5
lines changed

doc/describe/classes.adoc

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ It takes three arguments: the `struct` name, a list of base classes
3333
(empty in our example), and a list of (public) members by name (this includes
3434
both data members and member functions.)
3535

36-
Since `BOOST_DESCRIBE_STRUCT` is placed outside the type, it's non-intrisive,
36+
Since `BOOST_DESCRIBE_STRUCT` is placed outside the type, it's non-intrusive,
3737
does not require access to the definition, and can therefore be used to describe
3838
third-party types or types defined in system headers.
3939

@@ -187,3 +187,31 @@ public:
187187

188188
The case where a member function and a static member function have the same name
189189
and the same function type is currently not supported.
190+
191+
192+
## Avoiding empty variadic macro
193+
194+
The `BOOST_DESCRIBE_STRUCT` and `BOOST_DESCRIBE_CLASS` utilize empty variadic
195+
macro arguments for missing bases or members, which relies on a C++20
196+
preprocessor extension.
197+
198+
If this behavior is undesired or unsupported by your toolchain,
199+
you can avoid it by explicitly specifying `void` for base classes
200+
in simple cases, and by using the lower-level `BOOST_DESCRIBE_...`
201+
macros for classes, as shown below:
202+
203+
```_EMPTY
204+
struct X { int foo; };
205+
BOOST_DESCRIBE_STRUCT(X, (void), (foo))
206+
207+
class Y {
208+
public:
209+
int foo;
210+
private:
211+
int bar;
212+
friend BOOST_DESCRIBE_BASES(Y, void)
213+
friend BOOST_DESCRIBE_PUBLIC_MEMBERS(Y, foo)
214+
friend BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(Y) // use _EMPTY suffix for missing member groups
215+
friend BOOST_DESCRIBE_PRIVATE_MEMBERS(Y, bar)
216+
};
217+
```

include/boost/describe/class.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ namespace describe
3737
static_assert(std::is_class<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_STRUCT should only be used with class types"); \
3838
BOOST_DESCRIBE_BASES(C, BOOST_DESCRIBE_PP_UNPACK Bases) \
3939
BOOST_DESCRIBE_PUBLIC_MEMBERS(C, BOOST_DESCRIBE_PP_UNPACK Members) \
40-
BOOST_DESCRIBE_PROTECTED_MEMBERS(C) \
41-
BOOST_DESCRIBE_PRIVATE_MEMBERS(C)
40+
BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(C) \
41+
BOOST_DESCRIBE_PRIVATE_MEMBERS_EMPTY(C)
4242

4343
#else
4444

@@ -63,8 +63,8 @@ namespace describe
6363
static_assert(std::is_class<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_STRUCT should only be used with class types"); \
6464
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_BASES_(C BOOST_DESCRIBE_PP_UNPACK Bases) \
6565
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PUBLIC_MEMBERS_(C BOOST_DESCRIBE_PP_UNPACK Members) \
66-
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PROTECTED_MEMBERS_(C) \
67-
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PRIVATE_MEMBERS_(C)
66+
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(C) \
67+
BOOST_DESCRIBE_MAYBE_UNUSED BOOST_DESCRIBE_PRIVATE_MEMBERS_EMPTY(C)
6868

6969
#endif
7070

include/boost/describe/detail/bases.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ template<class C, class B> struct base_descriptor
2626
static constexpr unsigned modifiers = compute_base_modifiers<C, B>();
2727
};
2828

29+
template<class C>
30+
struct base_descriptor<C, void>
31+
{
32+
using type = void;
33+
static constexpr unsigned modifiers = 0U;
34+
};
35+
2936
#ifndef __cpp_inline_variables
3037
template<class C, class B> constexpr unsigned base_descriptor<C, B>::modifiers;
3138
#endif

include/boost/describe/detail/members.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,15 @@ template<class C, class F> constexpr auto mfn( F * p ) { return p; }
7777

7878
#endif
7979

80+
#define BOOST_DESCRIBE_PUBLIC_MEMBERS_EMPTY(C) inline auto boost_public_member_descriptor_fn( C** ) \
81+
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_public>( 0 ); }
82+
83+
#define BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(C) inline auto boost_protected_member_descriptor_fn( C** ) \
84+
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_protected>( 0 ); }
85+
86+
#define BOOST_DESCRIBE_PRIVATE_MEMBERS_EMPTY(C) inline auto boost_private_member_descriptor_fn( C** ) \
87+
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_private>( 0 ); }
88+
8089
} // namespace detail
8190
} // namespace describe
8291
} // namespace boost

test/Jamfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,9 @@ run pedantic_bases_test.cpp
8383
run pedantic_members_test.cpp
8484
: : : <warnings>pedantic ;
8585

86+
run pedantic_empty_descriptor_test.cpp
87+
: : : <warnings>pedantic ;
88+
8689
run enum_from_string_test2.cpp ;
8790

8891
# examples
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2022 Peter Dimov
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#include <boost/describe/members.hpp>
6+
#include <boost/describe/class.hpp>
7+
#include <boost/core/lightweight_test_trait.hpp>
8+
9+
#if !defined(BOOST_DESCRIBE_CXX14)
10+
11+
#include <boost/config/pragma_message.hpp>
12+
13+
BOOST_PRAGMA_MESSAGE("Skipping test because C++14 is not available")
14+
int main() {}
15+
16+
#else
17+
18+
#if defined(__clang__)
19+
#pragma clang diagnostic push
20+
#pragma clang diagnostic error "-Wgnu-zero-variadic-macro-arguments"
21+
#endif
22+
23+
24+
// Using (void) to specify empty bases for simple structs
25+
struct X { int foo; };
26+
BOOST_DESCRIBE_STRUCT(X, (void), (foo))
27+
28+
// Using underlying macros for classes or empty types
29+
class Y {
30+
public:
31+
int foo;
32+
private:
33+
int bar;
34+
friend BOOST_DESCRIBE_BASES(Y, void)
35+
friend BOOST_DESCRIBE_PUBLIC_MEMBERS(Y, foo)
36+
friend BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(Y)
37+
friend BOOST_DESCRIBE_PRIVATE_MEMBERS(Y, bar)
38+
};
39+
40+
41+
int main()
42+
{
43+
using namespace boost::describe;
44+
using namespace boost::mp11;
45+
46+
{
47+
using L = describe_bases<X, mod_any_access>;
48+
BOOST_TEST_EQ( mp_size<L>::value, 0 );
49+
}
50+
51+
{
52+
using L = describe_members<X, mod_any_access>;
53+
BOOST_TEST_EQ( mp_size<L>::value, 1 );
54+
}
55+
56+
{
57+
using L = describe_bases<Y, mod_any_access>;
58+
BOOST_TEST_EQ( mp_size<L>::value, 0 );
59+
}
60+
61+
{
62+
using L = describe_members<Y, mod_any_access>;
63+
BOOST_TEST_EQ( mp_size<L>::value, 2 );
64+
}
65+
66+
return boost::report_errors();
67+
}
68+
69+
70+
71+
#endif

0 commit comments

Comments
 (0)