'Get base class for a type in class hierarchy
Is is possible to get base class type in a class hierarchy?
For example:
struct A{};
struct B{} : public A;
struct C{} : public B;
I want some template that will have typedef Base<T>::Type inside like this:
Base<A>::Type == A
Base<B>::Type == A
Base<C>::Type == A
Is this possible? What about the case when I have multiple inheritance?
Solution 1:[1]
Classes in C++ can have more than one base class, so there's no sense in having a "get me the base" trait.
However, the TR2 additions include new compiler-supported traits std::tr2::bases and std::tr2::direct_bases, which returns an opaque type list of base classes.
I'm not sure whether this will make it into C++14, or whether it'll be released independently, but GCC already seems to support this.
Solution 2:[2]
This might be a nice way to do it, depending on your use case. Declare a typedef of the base class named base in the base class itself.
Then derived classes X will inherit it as the typename X::base.
So B::base is A, and C::base is A.
struct A
{
typedef A base;
};
struct B : A {};
struct C : B {};
template<class X>
void f()
{
typename X::base x;
}
int main()
{
f<B>();
f<C>();
}
Solution 3:[3]
With certain limitations, it's possible!
Each base that needs to be detectable in this manner has to inherit from a certain CRTP base. (Or, possibly, contain some kind of macro.)
You get a list of all parents, including indirect ones.
#include <cstddef>
#include <iostream>
#include <typeindex>
#include <utility>
template <typename T>
struct tag
{
using type = T;
};
template <typename ...P>
struct type_list
{
inline static constexpr std::size_t size = sizeof...(P);
};
namespace impl
{
constexpr void adl_ViewBase() {} // A dummy ADL target.
template <typename D, std::size_t I>
struct BaseViewer
{
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-template-friend"
#endif
friend constexpr auto adl_ViewBase(BaseViewer);
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
};
template <typename D, std::size_t I, typename B>
struct BaseWriter
{
friend constexpr auto adl_ViewBase(BaseViewer<D, I>) {return tag<B>{};}
};
template <typename D, typename Unique, std::size_t I = 0, typename = void>
struct NumBases : std::integral_constant<std::size_t, I> {};
template <typename D, typename Unique, std::size_t I>
struct NumBases<D, Unique, I, decltype(adl_ViewBase(BaseViewer<D, I>{}), void())> : std::integral_constant<std::size_t, NumBases<D, Unique, I+1, void>::value> {};
template <typename D, typename B>
struct BaseInserter : BaseWriter<D, NumBases<D, B>::value, B> {};
template <typename T>
constexpr void adl_RegisterBases(void *) {} // A dummy ADL target.
template <typename T>
struct RegisterBases : decltype(adl_RegisterBases<T>((T *)nullptr), tag<void>())
{};
template <typename T, typename I>
struct BaseListLow {};
template <typename T, std::size_t ...I>
struct BaseListLow<T, std::index_sequence<I...>>
{
static constexpr type_list<decltype(adl_ViewBase(BaseViewer<T, I>{}))...> helper() {}
using type = decltype(helper());
};
template <typename T>
struct BaseList : BaseListLow<T, std::make_index_sequence<(impl::RegisterBases<T>{}, NumBases<T, void>::value)>> {};
}
template <typename T>
using base_list = typename impl::BaseList<T>::type;
template <typename T>
struct Base
{
template <
typename D,
std::enable_if_t<std::is_base_of_v<T, D>, std::nullptr_t> = nullptr,
typename impl::BaseInserter<D, T>::nonExistent = nullptr
>
friend constexpr void adl_RegisterBases(void *) {}
};
struct A : Base<A> {};
struct B : Base<B>, A {};
struct C : Base<C> {};
struct D : Base<D>, B, C {};
template <typename T>
void printType()
{
#ifndef _MSC_VER
std::cout << __PRETTY_FUNCTION__ << '\n';
#else
std::cout << __FUNCSIG__ << '\n';
#endif
};
int main()
{
static_assert( base_list<D>::size == 4 );
printType<base_list<D>>(); // typeList<tag<A>, tag<B>, tag<C>, tag<D>>, order may vary
}
Here's what's going on:
- You use stateful template metaprogramming to create a list of types, which you can append types to by instantiating a specific template.
- Using a CRTP base, you add a
friendfunction to each class that needs to be detectable in this manner. Those functions are made uncallable with SFINAE, but merely considering them during overload resolution instantiates the template that appends the corresponding base class to the list. - You invoke this overloaded function with ADL, and since overloads from all bases are considered and are attempted to be instantiated, you get a list of bases.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | |
| Solution 3 |
