Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion doc/describe/classes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ It takes three arguments: the `struct` name, a list of base classes
(empty in our example), and a list of (public) members by name (this includes
both data members and member functions.)

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

Expand Down Expand Up @@ -187,3 +187,31 @@ public:

The case where a member function and a static member function have the same name
and the same function type is currently not supported.


## Avoiding empty variadic macro

The `BOOST_DESCRIBE_STRUCT` and `BOOST_DESCRIBE_CLASS` utilize empty variadic
macro arguments for missing bases or members, which relies on a C++20
preprocessor extension.

If this behavior is undesired or unsupported by your toolchain,
you can avoid it by explicitly specifying `void` for base classes
in simple cases, and by using the lower-level `BOOST_DESCRIBE_...`
macros for classes, as shown below:

```_EMPTY
struct X { int foo; };
BOOST_DESCRIBE_STRUCT(X, (void), (foo))

class Y {
public:
int foo;
private:
int bar;
friend BOOST_DESCRIBE_BASES(Y, void)
friend BOOST_DESCRIBE_PUBLIC_MEMBERS(Y, foo)
friend BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(Y) // use _EMPTY suffix for missing member groups
friend BOOST_DESCRIBE_PRIVATE_MEMBERS(Y, bar)
};
```
8 changes: 4 additions & 4 deletions include/boost/describe/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ namespace describe
static_assert(std::is_class<C>::value || std::is_union<C>::value, "BOOST_DESCRIBE_STRUCT should only be used with class types"); \
BOOST_DESCRIBE_BASES(C, BOOST_DESCRIBE_PP_UNPACK Bases) \
BOOST_DESCRIBE_PUBLIC_MEMBERS(C, BOOST_DESCRIBE_PP_UNPACK Members) \
BOOST_DESCRIBE_PROTECTED_MEMBERS(C) \
BOOST_DESCRIBE_PRIVATE_MEMBERS(C)
BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(C) \
BOOST_DESCRIBE_PRIVATE_MEMBERS_EMPTY(C)

#else

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

#endif

Expand Down
7 changes: 7 additions & 0 deletions include/boost/describe/detail/bases.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ template<class C, class B> struct base_descriptor
static constexpr unsigned modifiers = compute_base_modifiers<C, B>();
};

template<class C>
struct base_descriptor<C, void>
{
using type = void;
static constexpr unsigned modifiers = 0U;
};

#ifndef __cpp_inline_variables
template<class C, class B> constexpr unsigned base_descriptor<C, B>::modifiers;
#endif
Expand Down
9 changes: 9 additions & 0 deletions include/boost/describe/detail/members.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ template<class C, class F> constexpr auto mfn( F * p ) { return p; }

#endif

#define BOOST_DESCRIBE_PUBLIC_MEMBERS_EMPTY(C) inline auto boost_public_member_descriptor_fn( C** ) \
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_public>( 0 ); }

#define BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(C) inline auto boost_protected_member_descriptor_fn( C** ) \
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_protected>( 0 ); }

#define BOOST_DESCRIBE_PRIVATE_MEMBERS_EMPTY(C) inline auto boost_private_member_descriptor_fn( C** ) \
{ return boost::describe::detail::member_descriptor_fn_impl<boost::describe::mod_private>( 0 ); }

} // namespace detail
} // namespace describe
} // namespace boost
Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ run pedantic_members_test.cpp

run enum_from_string_test2.cpp ;

run empty_descriptor_test.cpp ;

# examples

obj describe_cxx14 : describe_cxx14.cpp ;
Expand Down
71 changes: 71 additions & 0 deletions test/empty_descriptor_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright 2022 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/describe/members.hpp>
#include <boost/describe/class.hpp>
#include <boost/core/lightweight_test_trait.hpp>

#if !defined(BOOST_DESCRIBE_CXX14)

#include <boost/config/pragma_message.hpp>

BOOST_PRAGMA_MESSAGE("Skipping test because C++14 is not available")
int main() {}

#else

#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic error "-Wgnu-zero-variadic-macro-arguments"
#endif


// Using (void) to specify empty bases for simple structs
struct X { int foo; };
BOOST_DESCRIBE_STRUCT(X, (void), (foo))

// Using underlying macros for classes or empty types
class Y {
public:
int foo;
private:
int bar;
friend BOOST_DESCRIBE_BASES(Y, void)
friend BOOST_DESCRIBE_PUBLIC_MEMBERS(Y, foo)
friend BOOST_DESCRIBE_PROTECTED_MEMBERS_EMPTY(Y)
friend BOOST_DESCRIBE_PRIVATE_MEMBERS(Y, bar)
};


int main()
{
using namespace boost::describe;
using namespace boost::mp11;

{
using L = describe_bases<X, mod_any_access>;
BOOST_TEST_EQ( mp_size<L>::value, 0 );
}

{
using L = describe_members<X, mod_any_access>;
BOOST_TEST_EQ( mp_size<L>::value, 1 );
}

{
using L = describe_bases<Y, mod_any_access>;
BOOST_TEST_EQ( mp_size<L>::value, 0 );
}

{
using L = describe_members<Y, mod_any_access>;
BOOST_TEST_EQ( mp_size<L>::value, 2 );
}

return boost::report_errors();
}



#endif