Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflect Inheritance hierarchy #14

Closed
ccvca opened this issue Mar 28, 2020 · 2 comments
Closed

Reflect Inheritance hierarchy #14

ccvca opened this issue Mar 28, 2020 · 2 comments
Labels
question Further information is requested

Comments

@ccvca
Copy link

ccvca commented Mar 28, 2020

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
Copy link
Owner

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
Copy link
Author

ccvca commented Mar 28, 2020

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

@ccvca ccvca closed this as completed Mar 28, 2020
@veselink1 veselink1 added the question Further information is requested label Mar 30, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants