2.5. Reflection: Aggregates - TheNitesWhoSay/RareCpp GitHub Wiki

Aggregate Reflection Run

struct MyObj
{
    int a = 0;
    int b = 1;
    std::string c {};
};

int main()
{
    std::cout << Json::pretty(MyObj{}) << std::endl;

    RareTs::Members<MyObj>::forEach([&](auto member) {
        std::cout << member.index << ": " << member.name << std::endl;
    });
}

Certain members of aggregate types do not need any macro or any other form of registration in order to be reflected.

The members of such aggregates are automatically available through the regular RareCpp reflection interfaces.

Requirements & Limitations

  • RareCpp enables aggregate reflection only for C++20 and up (Clang 12+, GCC 11+, MSVC 19.29+)
  • Only certain aggregates may be reflected
    • No private/protected members
    • No base-classes/inheritance
    • No virtual classes
  • Reflect up to 121 members
  • Only small C-arrays members may be included
  • Only instance-data members are reflected, statics and functions are not included
  • No adaptive structures - and ergo no use of RareBuilder, RareTs::MemberType, RareTs::whitebox, etc.

How?

C++ allows for structured-bindings to data members, if you can get the count of members - as can be done with recurisve checks of what initializations are possible - then you can specify a structured binding to your type and thus get value references & member types.

From there, the std::addressof member references can be fed as a template parameter to a function where you use compiler macros like __PRETTY_FUNCTION__ or __FUNCSIG__ (or std::source_location) to get a string representing the signature of the function, since the signature of the function includes the address of/pointer to a member, the compilers (or at least, all major compilers) include the actual name of the member, which is parsed and returned.

Putting that together (as can be seen in code detail here), you have the types, values, and names of the members of certain aggregates.

⚠️ **GitHub.com Fallback** ⚠️