[#MRDOCS_DESCRIBE_STRUCT] = MRDOCS_DESCRIBE_STRUCT :mrdocs: Describe the bases and members of a class, outside its definition. == Synopsis Declared in `<mrdocs/Support/Reflection/Describe.hpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- #define MRDOCS_DESCRIBE_STRUCT(C, Bases, Members) ---- == Description 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: [,cpp] ---- 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): [,cpp] ---- // MRDOCS_DESCRIBE_STRUCT(Shape, (), (name)) static_assert(std::is_class_v || std::is_union_v, "..."); [[maybe_unused]] typename bases_descriptor_impl>::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 || std::is_union_v, "..."); // 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>::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 || std::is_union_v, "..."); [[maybe_unused]] typename bases_descriptor_impl>::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: [,cpp] ---- namespace describe = mrdocs::describe; static_assert(describe::described); static_assert(describe::describedMemberCount() == 3); void print(geo::Point const& p) { // Own members only: x, y describe::for_each( describe::describe_members{}, [&](auto d) { std::cout << d.name << " = " << p.*d.pointer << '\n'; }); // Direct bases only: Shape describe::for_each( describe::describe_bases{}, [](auto d) { using Base = typename decltype(d)::type; static_assert(std::is_same_v); }); // 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. == Parameters [cols="1,4"] |=== | Name| Description | *C* | The class type. | *Bases* | The parenthesized list of direct base classes. | *Members* | The parenthesized list of data member names. |=== [.small]#Created with https://www.mrdocs.com[MrDocs]#