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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With