[#MRDOCS_DESCRIBE_CLASS] = MRDOCS_DESCRIBE_CLASS :mrdocs: Describe the bases and members of a class, inside its definition. == Synopsis Declared in `<mrdocs/Support/Reflection/Describe.hpp>` [source,cpp,subs="verbatim,replacements,macros,-callouts"] ---- #define MRDOCS_DESCRIBE_CLASS(C, Bases, Members) ---- == 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: [,cpp] ---- namespace app { struct Named { std::string name; }; MRDOCS_DESCRIBE_STRUCT(Named, (), (name)) template 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): [,cpp] ---- // MRDOCS_DESCRIBE_CLASS(Counter, (Named), (value_, hits_)) // Hidden friends: only ADL on a `Counter**` 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>::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>::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: [,cpp] ---- namespace describe = mrdocs::describe; static_assert(describe::described>); static_assert(describe::describedMemberCount>() == 3); void dump() { app::Counter 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 ..."). * `C` names the enclosing class. In a template, the injected name (`Counter`) is enough; you don't need to spell `Counter<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 `Bases` and `Members` are the same as for `MRDOCS_DESCRIBE_STRUCT`: direct bases only, each described too if you walk inherited members, and up to 128 data members. == 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]#