veselink1 / refl-cpp

Static reflection for C++17 (compile-time enumeration, attributes, proxies, overloads, template functions, metaprogramming).
https://veselink1.github.io/refl-cpp/md__introduction.html
MIT License
1.06k stars 77 forks source link

Reflect Inheritance hierarchy #14

Closed ccvca closed 4 years ago

ccvca commented 4 years ago

Is it possible to iterate over the base classes in any way? I'm thinking about something like this:

template <typename T>
void myBindMembers(T *instance)
{
    for_each(refl::reflect<T>().baseClasses, [&](auto baseClass) {
        // Call recursive to catch all
        myBindMembers((typename decltype(baseClass)::value_type*) instance);        
    })
    // Access Attributes of base Type
    // Bind all members defined in the instance and it's base classes.

}

struct MyBase{
    int BaseA;
    int BaseB;
};

REFL_TYPE(MyBase, attr("Attribute for BaseA and BaseB"))
REFL_FIELD(BaseA)
REFL_FIELD(BaseB)
REFL_END

struct Sub{
    int SubC;
};

REFL_TYPE(Sub, attr("Attribute for SubC"))
// Base classes are defined at the beginning and terminated (so the counter can be reused)
REFL_BASE(MyBase)
// Possible more Base classs definitions.
REFL_BASE_END
REFL_FIELD(SubC)
REFL_END

Or is this possible using attributes? I didn't find a solution for this, as "get_attribute" does not accept a template type like BaseClass.

If there is a ForEach for attributes, then it could be combined with this https://stackoverflow.com/a/21512908 to build something equivalent out of attributes. But I couldn't find a way to iterate over attributes either.

PS: You've build a great library.

veselink1 commented 4 years ago

Hello,

Yes, that is entirely doable through a combination of an attribute of a variadic template type and refl::util::for_each. There is no built-in way in C++ to get that information, so someone will have to provide it manually (proposal for it seems to have been rejected https://stackoverflow.com/a/33664204).

Currently there is a bug in the built-in refl::attr::base_types, so I would recommend not using it. I expect that it will end up being replaced by something more useful.

Instead, you could create your own attribute like so:

template <typename... Ts>
struct Bases : public refl::attr::usage::type {
    static constexpr refl::type_list<refl::type_descriptor<Ts>...> descriptors;
};

And use it like this:

constexpr auto bases = refl::descriptor::get_attribute<Bases>(refl::reflect<T>());
if constexpr (bases.descriptors.size) {
    for_each(bases.descriptors, [](auto t) {
        std::cout << t.name << '\n';
    });
}

"get_attribute" does not accept a template type like BaseClass

get_attribute has an overload that accepts a variadic Attribute<Ts...> as well. It is not available unprefixed (like for_each) since ADL does not kick-in for names with explicitly specified types.

If there is a ForEach for attributes

for_each iterates over type_list<Ts...> only, but one could use refl::trait::as_type_list_t to convert any T<Ts..> (like std::tuple<Ts...>) into type_list<Ts...>.

PS: You've build a great library.

Thanks.

ccvca commented 4 years ago

Many thanks, this library is more powerful than I expected :).

If anyone needs something similar, here is a complete example: (Tested using v0.8.1)

#include <iostream>
#include <refl.hpp>
#include <string>

template <typename... Ts>
struct Bases : public refl::attr::usage::type {
    static constexpr refl::type_list<refl::type_descriptor<Ts>...> descriptors = {};
};

struct Base1_t{
    std::string Base1A;
    std::string Base1B;
};

REFL_TYPE(Base1_t)
REFL_FIELD(Base1A)
REFL_FIELD(Base1B)
REFL_END

struct Base2_t{
    std::string Base2A;
    std::string Base2B;
};

REFL_TYPE(Base2_t)
REFL_FIELD(Base2A)
REFL_FIELD(Base2B)
REFL_END

struct Sub_t : public Base1_t, public Base2_t{
    std::string SubA;
};

REFL_TYPE(Sub_t, Bases<Base1_t, Base2_t>())
REFL_FIELD(SubA)
REFL_END

template<typename T>
void useIt(T *instance);

template<typename T>
void useIt(T *instance)
{
    if constexpr(refl::descriptor::has_attribute<Bases>(refl::reflect<T>())){
        constexpr auto bases = refl::descriptor::get_attribute<Bases>(refl::reflect<T>());
        if constexpr (bases.descriptors.size) {
        refl::util::for_each(bases.descriptors, [&](auto t) {
            std::cout << "Base: " << t.name << std::endl;
            useIt(static_cast<typename decltype(t)::type*>(instance));
        });
        }
    }
    std::cout << "Type: " << refl::reflect<T>().name << std::endl;
   for_each(refl::reflect<T>().members, [&](auto member) {
        std::cout << member.name << " = " << member(*instance) << std::endl;
    });
}

int main(int argc, char* argv[])
{
    Sub_t sub;
    sub.SubA = "SubA";
    sub.Base1A = "Base1A";
    sub.Base1B = "Base1B";
    sub.Base2A = "Base2A";
    sub.Base2B = "Base2B";

    std::cout << "TestReflCppBaseClass" << std::endl;
    useIt(&sub);
    std::cout << "TestReflCppBaseClass End" << std::endl;
    return 0;
}

Output:

TestReflCppBaseClass
Base: Base1_t
Type: Base1_t
Base1A = Base1A
Base1B = Base1B
Base: Base2_t
Type: Base2_t
Base2A = Base2A
Base2B = Base2B
Type: Sub_t
SubA = SubA
TestReflCppBaseClass End