prestokeys
prestokeys

Reputation: 4849

Detecting if two classes are siblings

Can someone improve on my horrible implementation of are_siblings below? It is merely guessing at all possible parents and then deciding if the two classes are siblings based on those parent candidates.

#include <type_traits>

template <bool...> struct Or;

template <bool First, bool... Rest>
struct Or<First, Rest...> : std::conditional<First,
    std::true_type, Or<Rest...>>::type {};

template <>
struct Or<> : std::false_type {};

template <typename T, typename U, typename... Parents>
struct are_siblings :
    Or<(std::is_base_of<Parents, T>::value && std::is_base_of<Parents, U>::value
    && !std::is_base_of<T,U>::value && !std::is_base_of<U,T>::value)...> {};

// Test
#include <iostream>

struct A {};
struct B : A {};
struct C : A {};
struct D : A, C {};
struct E : B, C {};

int main() {
    std::cout << std::boolalpha << are_siblings<D,E, A,B,C>::value << '\n';  // true
    std::cout << are_siblings<C,D, A,B,E>::value << '\n';  // false
    std::cout << are_siblings<B,E, A,B,E>::value << '\n';  // false
}

If there is a way to obtain all parents of a class at compile-time, then that would solve the problem I think.

Update: Below removes the need to guess at parent candidates, but ideally is there a solution that does not depend on classes defining a parents member type?

#include <type_traits>
#include <tuple>

template <bool...> struct Or;

template <bool First, bool... Rest>
struct Or<First, Rest...> : std::conditional<First,
    std::true_type, Or<Rest...>>::type {};

template <>
struct Or<> : std::false_type {};

template <typename T, typename U, typename Parents> struct are_siblings_helper;

template <typename T, typename U, template <typename...> class P, typename... Parents>
struct are_siblings_helper<T, U, P<Parents...>> :
    Or<(std::is_base_of<Parents, T>::value && std::is_base_of<Parents, U>::value
    && !std::is_base_of<T,U>::value && !std::is_base_of<U,T>::value)...> {};

template <typename, typename> struct merge;

template <template <typename...> class P, typename... Ts, typename... Us>
struct merge<P<Ts...>, P<Us...>> {
    using type = P<Ts..., Us...>;
};

template <typename T, typename U>
struct are_siblings : are_siblings_helper<T,U,
    typename merge<typename T::parents, typename U::parents>::type> {};

// Test
#include <iostream>

struct A {};

struct B : virtual A {
    using parents = std::tuple<A>;
};

struct C : virtual A {
    using parents = std::tuple<A>;
};

struct D : virtual A, C {
    using parents = std::tuple<A,C>;
};

struct E : virtual B, virtual C {
    using parents = std::tuple<B,C>;
};

int main() {
    std::cout << std::boolalpha << are_siblings<D,E>::value << '\n';  // true
    std::cout << are_siblings<C,D>::value << '\n';  // false
    std::cout << are_siblings<B,E>::value << '\n';  // false
}

Further update: If you are interested, you can check where this project is heading with this updated code:
http://ideone.com/UQJ3WZ

std::is_base_of is not even being used at all anymore! It looks to be a major project for completion that I'll try to finish on my own.

Upvotes: 0

Views: 189

Answers (1)

Tony Delroy
Tony Delroy

Reputation: 106068

"is there a solution that does not depend on classes defining a parents member type" - nothing that's typically more convenient in pure C++. (Other options amount to recording the same kind of information your parents member adds less directly - e.g. in type traits; search for "C++ introspection library" or "C++ reflection library" if you want to see what people have hacked up).

There are tools that can output Abstract Syntax Tree representations (e.g. clang/llvm), which can then be read/processed by tools that create C++ code, but it's relatively rarely worth bringing them in to your build process....

Upvotes: 1

Related Questions