Metaobject using "static virtual" functions

I developed some kind of mechanism for meta objects in the project to associate type names and properties of any type with an object (see the result here ). My dirty code is working, and I'm trying to make it cleaner. Given the following dummy structures:

struct A
{
    using Self = A;
    using Base = void;

    static std::string get_type_name(){ return { "A" }; }

    static std::vector<int> get_properties(){ return { 0 }; }
};

#define SELF(class_name)\
    using Base = Self;\
    using Self = class_name;

struct AA : A
{
    SELF(AA)

    static std::string get_type_name() { return { "AA" }; }
};

struct AAA : AA
{
    SELF(AAA)

    static std::string get_type_name(){ return { "AAA" }; }

    static std::vector<int> get_properties(){ return { 2, 1 }; }
};

I ended up with this code to get object type names from its hierarchy:

// when the type has no Base member:
template<
    typename T,
    typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
    return { T::Self::get_type_name() };
}

// when the type has a Base member:
template<
    typename T,
    typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
typename std::vector<decltype(T::Self::get_type_name())> get_type_names()
{
    auto data = get_type_names<typename T::Base>();
    data.insert(data.begin(), T::Self::get_type_name());
    return data;
}

And something similar for properties:

template<
    typename T,
    typename std::enable_if<std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
    return { T::Self::get_properties() };
}

template<
    typename T,
    typename std::enable_if<!std::is_same<typename T::Base, void>::value>::type* = nullptr
>
decltype(T::Self::get_properties()) get_properties()
{
    auto data = get_properties<typename T::Base>();
    auto self_data = T::Self::get_properties();
    data.insert(data.begin(), self_data.begin(), self_data.end());
    return data;
}

When testing the code using this snippet:

template<typename T>
void print()
{
    std::cout << T::get_type_name() << std::endl << "\thas types:" << std::endl;
    for(auto type_name : get_type_names<T>())
    {
        std::cout << "\t\t" << type_name << std::endl;
    }
    std::cout << "\thas properties:" << std::endl;
    for(auto property : get_properties<T>())
    {
        std::cout << "\t\t" << property << std::endl;
    }
}

int main()
{
    print<A>();
    print<AA>();
    print<AAA>();

    return 0;
}

I get the following output:

A
    has types:
        A
    has properties:
        0
AA
    has types:
        AA
        A
    has properties:
        0
        0
AAA
    has types:
        AAA
        AA
        A
    has properties:
        2
        1
        0
        0

This first prototype works well for names, but as soon as an object is declared without properties, those from its base are duplicated. Does anyone see an easy way to fix the problem?

: - , - , ?

+3
1

, . -, . , :

template <class... > struct typelist { };

template <class T, class... Bases>
struct get_bases
    : get_bases<typename T::Base, Bases..., T>
{ };

template <class... Bases>
struct get_bases<void, Bases...>
{
    using type = typelist<Bases...>;
};

template <class T>
using get_bases_t = typename get_bases<T>::type;

, get_bases_t<A> - typelist<A>, get_bases_t<AAA> - typelist<AAA, AA, A>. , - . :

template <class T> struct tag { using type = T; };

template <class... Ts>
auto for_each(typelist<Ts...>) {
    return [](auto&& f){
        using swallow = int[];
        (void)swallow{0,
            (void(f(tag<Ts>{})), 0)...
        };
    };
}

- get_properties(), . , , :

template <class T>
std::vector<int> get_properties() {
    using bases = get_bases_t<T>;

    using F = std::vector<int>(*)();
    F last = nullptr;
    std::vector<int> props;

    for_each(bases{})([&](auto t){
        using B = typename decltype(t)::type;
        F cur = B::get_properties;
        if (cur != last) {
            auto next = cur();
            props.insert(props.end(), next.begin(), next.end());
            last = cur;
        }
    });

    return props;
}

:

template <class T>
std::vector<std::string> get_type_names()
{
    using bases = get_bases_t<T>;

    std::vector<std::string> names;
    names.reserve(len_v<bases>); // TODO

    for_each(bases{})([&](auto t){
        names.push_back(decltype(t)::get_type_name());
    });

    return names;
}

, , :

template <class... Bs>
std::vector<std::string> get_type_names_impl(typelist<Bs...>) {
    return {Bs::get_type_name()...};
}


template <class T>
std::vector<std::string> get_type_names()
{
    return get_type_names_impl(get_bases_t<T>);
}
+1

Source: https://habr.com/ru/post/1654209/


All Articles