I am trying to implement my own std::is_base_of
for my AVR programming (avr-gcc does not yet support <type_traits>
. I took inspiration of the possible implementation on the cppreference page, it worked for a single type check. However, what I want to achieve is a statically executed validity checks of multiple types' heritage of one base class.
For simplicity I am using std::is_base_of
for the actual check below, however my actual solution is close to what is in the cppreference page linked above.
I will use it for tag dispatching, more specifically to allow option tags in any order.
Option Tags
struct tOption {};
struct tThis : public tOption {};
struct tThat : public tOption {};
struct tElse {}; // Wrongly defined option tag!
Single Heritage Validator Struct
template<typename TBase, typename TCandidate>
struct isBaseOf {
isBaseOf() = delete;
static const bool value = std::is_base_of<TBase, TCandidate>::value;
};
static_assert(isBaseOf<tOption, tThat>::value, "Invalid option tag!"); // OK!
static_assert(isBaseOf<tOption, tElse>::value, "Invalid option tag!"); // ERROR! Invalid option tag!
Attempt on Multiple Checks (addition to the isBaseOf
declaration above)
template<typename TBase, typename TCandidate, typename... TRest>
struct isBaseOf {
isBaseOf() = delete;
static const bool value = isBaseOf<TBase, TRest...>::value &&
std::is_base_of<TBase, TCandidate>::value;
};
This does not work. From what I see, I am not able to redeclare a template using a different number of types. However, I need at least two types in the last template construct. I tried with TBase as only parameter and just set the value to true, but the same issue is still here: error: redeclared with 3 template parameters
Usage
As mentioned, this is limited to a single check. Since my class (not shown here) uses variadic templates for any number of option tags (and avr-gcc does not support full c++14 with for-loops in constexpr
functions), I want to be able to use parameter unpacking and still check that all option tags have heritage of my base tag (tOption
).
template<typename... TOptions>
class tMyClass {
static_assert(isBaseOf<tOption, TOptions...>::value, "Invalid option tag(s)!"); // <--- THIS
// ...
};
Using Functions - Ugly and unwanted
I got it to work using a function instead of another struct, but I think this is confusing. I'd rather have one way of solving the issue throughout the recursive (static) stack. Also, this forces me to construct each tag, which is not very neat IMO.
template<typename TBase, typename TCandidate>
constexpr bool isBaseOf2(const TBase&, const TCandidate&) {
return std::is_base_of<TBase, TCandidate>::value;
}
template<typename TBase, typename TCandidate, typename... TRest>
constexpr bool isBaseOf2(const TBase& base, const TCandidate&, const TRest&... rest) {
return isBaseOf2(base, rest...) && std::is_base_of<TBase, TCandidate>::value;
}
static_assert(isBaseOf2(tOption{}, tThis{}, tThat{}), "Invalid option tag(s)!"); // OK!
static_assert(isBaseOf2(tOption{}, tThis{}, tElse{}), "Invalid option tag(s)!"); // ERROR! Invalid option tag(s)!
Is there any way to redefine a struct template with another number of parameters, such as in Attempt on Multiple Checks above?
Issue with
template<typename TBase, typename TCandidate, typename... TRest>
struct isBaseOf {
isBaseOf() = delete;
static const bool value = isBaseOf<TBase, TRest...>::value &&
std::is_base_of<TBase, TCandidate>::value;
};
Is that a the end, you finish with:
static const bool value = isBaseOf<TBase, /*Empty Pack*/>::value &&
std::is_base_of<TBase, TCandidate>::value;
isBaseOf<TBase, TRest...>
is invalid for empty pack.
You have to add specialization to handle this case:
template<typename TBase, typename TCandidate>
struct isBaseOf<TBase, TCandidate> {
isBaseOf() = delete;
static const bool value = std::is_base_of<TBase, TCandidate>::value;
};
Alternative without recursion:
template <bool... Bs> struct Bools{};
template <bool... Bs> using All = std::is_same<Bools<true, Bs...>, Bools<Bs..., true>>;
template<typename TBase, typename... TCandidates>
using isBaseOf = All<std::is_base_of<TBase, TCandidates>::value...>;