Describe the bases and members of a class, outside its definition.
Declared in <mrdocs/Support/Reflection/Describe.hpp>
#define MRDOCS_DESCRIBE_STRUCT(C, Bases, Members)
Place it at namespace scope, in the namespace of the class, after the class definition. Bases and Members are parenthesized, comma-separated lists, and either one may be empty:
namespace geo {
struct Shape
{
std::string name;
};
struct Point : Shape
{
int x = 0;
int y = 0;
};
struct Tag {};
MRDOCS_DESCRIBE_STRUCT(Shape, (), (name))
MRDOCS_DESCRIBE_STRUCT(Point, (Shape), (x, y))
MRDOCS_DESCRIBE_STRUCT(Tag, (), ())
} // namespace geo
Several bases are listed like several members, e.g. MRDOCS_DESCRIBE_STRUCT(AB, (A, B), (c)). The three lines expand to roughly the following (simplified: the ::mrdocs::describe::detail:: and ::mrdocs::describe:: qualifications are dropped and the assertion message is shortened):
// MRDOCS_DESCRIBE_STRUCT(Shape, (), (name))
static_assert(std::is_class_v<Shape> || std::is_union_v<Shape>, "...");
[[maybe_unused]]
typename bases_descriptor_impl<Shape, list<>>::type
mrdocs_base_descriptor_fn(Shape**);
[[maybe_unused]]
decltype(member_descriptor_fn_impl(
0,
member_descriptor<&Shape::name, []{ return "name"; }>{}))
mrdocs_member_descriptor_fn(Shape**);
// MRDOCS_DESCRIBE_STRUCT(Point, (Shape), (x, y))
// Rejects anything that isn't a class or a union.
static_assert(std::is_class_v<Point> || std::is_union_v<Point>, "...");
// Declared, never defined or called: only the return type matters.
// ADL finds it from a `Point**`, and the return type lists the bases.
[[maybe_unused]]
typename bases_descriptor_impl<Point, list<Shape>>::type
mrdocs_base_descriptor_fn(Point**);
// Same trick for the members: one descriptor per name, holding the
// member pointer and the name as a string.
[[maybe_unused]]
decltype(member_descriptor_fn_impl(
0,
member_descriptor<&Point::x, []{ return "x"; }>{},
member_descriptor<&Point::y, []{ return "y"; }>{}))
mrdocs_member_descriptor_fn(Point**);
// MRDOCS_DESCRIBE_STRUCT(Tag, (), ())
// Empty lists give an empty base list and no member descriptors.
static_assert(std::is_class_v<Tag> || std::is_union_v<Tag>, "...");
[[maybe_unused]]
typename bases_descriptor_impl<Tag, list<>>::type
mrdocs_base_descriptor_fn(Tag**);
[[maybe_unused]]
decltype(member_descriptor_fn_impl(0))
mrdocs_member_descriptor_fn(Tag**);
Afterwards the describe queries work on the type. Each member descriptor has a static pointer and name, and each base descriptor has a type alias:
namespace describe = mrdocs::describe;
static_assert(describe::described<geo::Point>);
static_assert(describe::describedMemberCount<geo::Point>() == 3);
void
print(geo::Point const& p)
{
// Own members only: x, y
describe::for_each(
describe::describe_members<geo::Point>{},
[&](auto d) {
std::cout << d.name << " = " << p.*d.pointer << '\n';
});
// Direct bases only: Shape
describe::for_each(
describe::describe_bases<geo::Point>{},
[](auto d) {
using Base = typename decltype(d)::type;
static_assert(std::is_same_v<Base, geo::Shape>);
});
// Inherited members too: name, x, y
describe::for_each_member(
p,
[](std::string_view name, auto const& value) {
std::cout << name << " = " << value << '\n';
});
}
Things to keep in mind:
The macro ends with its own ;, so don't add another one after it. An extra ; is an empty declaration that -Wextra-semi flags.
It has to be in the class's own namespace. The queries find the declarations by ADL, so they don't see them anywhere else.
List non-static data members by their unqualified names, up to 128 of them. &C::m is formed at namespace scope, so private and protected members fail to compile. For those, and for class templates, use MRDOCS_DESCRIBE_CLASS inside the class instead.
List direct bases only. for_each_member and describedMemberCount walk up the hierarchy themselves, which requires each listed base to be described too. Listing a type that isn't a base fails a static_assert once the bases are used.
| Name | Description |
|---|---|
| C | The class type. |
| Bases | The parenthesized list of direct base classes. |
| Members | The parenthesized list of data member names. |