MRDOCS_DESCRIBE_CLASS
Describe the bases and members of a class, inside its definition.
Description
Works like MRDOCS_DESCRIBE_STRUCT, but goes inside the class definition, after the members it names. It declares the descriptor functions as hidden friends, so it also works for class templates and for private members:
namespace app {
struct Named
{
std::string name;
};
MRDOCS_DESCRIBE_STRUCT(Named, (), (name))
template <class T>
class Counter : public Named
{
T value_{};
int hits_ = 0;
public:
explicit Counter(T v) : Named{"counter"}, value_(v) {}
MRDOCS_DESCRIBE_CLASS(Counter, (Named), (value_, hits_))
};
class Empty
{
public:
MRDOCS_DESCRIBE_CLASS(Empty, (), ())
};
} // namespace app
The two calls expand to roughly the following (simplified: the ::mrdocs::describe::detail:: and ::mrdocs::describe:: qualifications are dropped):
// MRDOCS_DESCRIBE_CLASS(Counter, (Named), (value_, hits_))
// Hidden friends: only ADL on a `Counter<T>**` finds them, and each
// instantiation of the template gets its own pair. Nothing calls
// them; the `{ return {}; }` bodies only keep GCC from warning about
// undefined internal functions.
friend
typename bases_descriptor_impl<Counter, list<Named>>::type
mrdocs_base_descriptor_fn(Counter**) { return {}; }
friend
decltype(member_descriptor_fn_impl(
0,
member_descriptor<&Counter::value_, []{ return "value_"; }>{},
member_descriptor<&Counter::hits_, []{ return "hits_"; }>{}))
mrdocs_member_descriptor_fn(Counter**) { return {}; }
// MRDOCS_DESCRIBE_CLASS(Empty, (), ())
friend
typename bases_descriptor_impl<Empty, list<>>::type
mrdocs_base_descriptor_fn(Empty**) { return {}; }
friend
decltype(member_descriptor_fn_impl(0))
mrdocs_member_descriptor_fn(Empty**) { return {}; }
With GCC, the two friends are also wrapped in _Pragma`s that silence `‐Wnon‐template‐friend for them.
Afterwards the same describe queries as for MRDOCS_DESCRIBE_STRUCT work on every instantiation, private members included:
namespace describe = mrdocs::describe;
static_assert(describe::described<app::Counter<double>>);
static_assert(describe::describedMemberCount<app::Counter<int>>() == 3);
void
dump()
{
app::Counter<int> c(42);
// Prints name = counter, value_ = 42, hits_ = 0
describe::for_each_member(
c,
[](std::string_view name, auto const& v) {
std::cout << name << " = " << v << '\n';
});
}
Things to keep in mind:
-
Put it after the member declarations. The friend return types aren't a complete‐class context, so a member declared below the macro isn't found ("no member named ...").
-
Cnames the enclosing class. In a template, the injected name (Counter) is enough; you don't need to spellCounter<T>. -
The access section it sits in doesn't matter, since friend declarations ignore access.
-
It ends with a function body, so it needs no trailing
;. -
The rules for
BasesandMembersare the same as forMRDOCS_DESCRIBE_STRUCT: direct bases only, each described too if you walk inherited members, and up to 128 data members.
Parameters
Name |
Description |
C |
The class type. |
Bases |
The parenthesized list of direct base classes. |
Members |
The parenthesized list of data member names. |
Created with MrDocs