Reputation: 16310
Suppose one has a class hierarchy, without multiple inheritance:
struct TOP{};
struct L : TOP{};
struct R : TOP{};
struct LL : L{};
struct LR : L{};
struct RL : R{};
struct RR : R{};
Is it possible to write a metafunction that will return the type of the common base of two types? (it could return void
if not common base class exists.)
For example
common_base<RR, R>::type == R
common_base<RL, RR>::type == R
common_base<LL, RR>::type == TOP
common_base<LL, std::string>::type == void
Obviously this wouldn't work with multiple inhertance, but I am focused in the the single inheritance case.
First, it doesn't seem to be possible without some introspection of the base class. So, I have this easier problem, do it in such a way that each clase knows its base (by an internal base
type), for example:
struct LR : L{using base = L;};
Even in this way, I cannot seem to get the metaprogramming right.
Also I read somewhere (I can't find it now) that GCC has some extensions to detect common base class. Is that the case?
Upvotes: 10
Views: 1456
Reputation: 4637
If you have each class alias the base as base
(like below), it can be done.
struct Child : Parent { using base = Parent; }; //typedef works too
I created a struct
:
template <class T1, class T2>
struct CommonBase;
CommonBase
works by comparing every base of T2
to T1
. When it reaches the top level base, it starts at the bottom again, but compares against the base of T1
.
For example: CommonBase<RL, RR>
would go through the following checks:
RL != RR
RL != R
RL != Top
R != RR
R == R
So CommonBase<RL, RR>::type == R
. If there is no common base, type == void
.
I put the code at the end because template metaprogramming is so pretty:
#include <type_traits>
template <class T>
struct GetBase //type = T::base, or else void
{
template <class TT> static typename TT::base& f(int);
template <class TT> static void f(...);
typedef std::remove_reference_t<decltype(f<T>(0))> type;
};
template <class T1, class T2>
struct Compare2 //Compares T1 to every base of T2
{
typedef typename GetBase<T2>::type _type;
template <class T, bool = !std::is_same<T, void>::value>
struct helper
{
typedef typename Compare2<T1, T>::type type;
};
template <class T>
struct helper<T, false>
{
typedef void type;
};
typedef typename helper<_type>::type type;
};
template <class T>
struct Compare2<T, T>
{
typedef T type;
};
template <class T1, class T2>
struct Compare1 //Uses Compare2 against every base of T1
{
typedef typename GetBase<T1>::type _type;
template <class T, bool = !std::is_same<T, void>::value>
struct helper
{
typedef typename Compare1<T, T2>::type type;
};
template <class T>
struct helper<T, false>
{
typedef void type;
};
typedef std::conditional_t<std::is_same<typename Compare2<T1, T2>::type, void>::value, typename helper<_type>::type, typename Compare2<T1, T2>::type> type;
};
template <class T>
struct Compare1<T, T> //Probably redundant
{
typedef T type;
};
template <class T1, class T2>
struct CommonBase //You can throw a std::enable_if on this to limit it to class types
{
typedef typename Compare1<T1, T2>::type type;
};
Here you can see it on some test cases.
Upvotes: 3
Reputation: 16940
There was at some point bases and direct_bases
in std::tr2 but that wasn't included. Some versions of gcc have it. Using these perhaps you can get what you want.
Upvotes: 3