2

I'm trying to check if a class that I'm templating is inheriting from another templated class, but I can't find the correct way to do it.

Right now I have the following:

#include <iostream>

template <typename MemberType, typename InterfaceType>
class Combination : public InterfaceType
{
public:
    Combination();
    virtual ~Combination();

private:

    MemberType*   pointer;
};


class MyInterface {};
class MyMember {};

class MyCombination : public Combination<MyMember, MyInterface>
{

};


int main()
{
    static_assert(std::is_base_of_v<Combination<MyMember, MyInterface>, MyCombination>);
    std::cout << "hello";
}

Which is working fine, but in the place this template is placed, I don't have access to the template parameters definitions, and this should be quite generic. What I'd like is to test for the MyCombination class to inherit from a generic Combination without taking into account the template arguments, something like this:

static_assert(std::is_base_of_v<Combination, MyCombination>);
or
static_assert(std::is_base_of_v<Combination<whatever, whatever>, MyCombination>);

Would you know how to check for this? In this case I can't use boost libraries or other external libraries.

Thanks!

EDIT: I don't have access to modify the Combination class, and what I can change is the assert and the My* classes.

AngelMixu
  • 83
  • 8
  • What is the underlying thing you are trying to solve? Code/implementation reuse? Maybe your issues have more to do with your design directions. (Some ideas here : https://onlinegdb.com/K0ow4stMv) – Pepijn Kramer Jul 19 '22 at 09:26
  • I am not sure if I understand your problem, `static_assert` will create a compile time error or not, but does not do anything in the executed code. So, it is not important where in the code it is placed, it does not have to be in `main`, it can be where you create an object of `MyCombination`, where `MyInterface` and `MyMember` should be known. – mch Jul 19 '22 at 09:30
  • @PepijnKramer I'm generating a macro for creating a class, and I'd like to check if the user of that class has a correct hierarchy, because I have to access elements from the Combination class, if the user of that class doesn't use the Combination class then I should report the error. The ideas you propose look good, but I can't modify the Combination class (I've just edited the post). Thanks! – AngelMixu Jul 19 '22 at 09:39
  • @mch yes, you're right, I put the assert there because... don't know, I was inspired like that when making the example :P In the official code the assert if just before the declaration of the template, inside a macro for generating the class. The problem in the official code is that I shouldn't have access to MyInterface and MyMember, as it's a very generic place. Thanks for the comment :) – AngelMixu Jul 19 '22 at 09:41
  • Does this answer your question? [checking if a class inherits from any template instantiation of a template](https://stackoverflow.com/questions/22592419/checking-if-a-class-inherits-from-any-template-instantiation-of-a-template) – Nimrod Jul 19 '22 at 09:41
  • @Nimrod I got several compile errors with that solution :( https://godbolt.org/z/jdj9hefq4 – AngelMixu Jul 19 '22 at 10:02
  • @AngelMixu Can you replace the macros with template functions too? Or are they concatenating text? You could also inherit the template class from an empty base class and check for inheritance of that. (Just so you know I usually base my designs on interface types not actual implementations. Something to do with testability, and code being able to communicate with said interface instead of an actual implementation. This will help refactorability). – Pepijn Kramer Jul 19 '22 at 10:03
  • Yes, I think I could do it, the design is quite modular, there are some macros here and there mixed with templates. But there's one macro that I can't replace because the architecture is designed that way. Thanks! good suggestions :) – AngelMixu Jul 19 '22 at 10:13

1 Answers1

2

This can easily be done with the C++20 concepts. Note that it requires a derived class to have exactly one public instantiated base class.

template <typename, typename InterfaceType>
class Combination : public InterfaceType {};

class MyInterface {};
class MyMember {};

class MyCombination : public Combination<MyMember, MyInterface> {};

template<class Derived, template<class...> class Base>
concept derived_from_template = requires (Derived& d) {
  []<typename... Ts>(Base<Ts...>&) {}(d);
};

static_assert(derived_from_template<MyCombination, Combination>);

Demo

The equivalent C++17 alternative would be

#include <type_traits>

template<template<class...> class Base, typename... Ts>
void test(Base<Ts...>&);

template<template<class...> class, class, class = void>
constexpr bool is_template_base_of = false;

template<template<class...> class Base, class Derived>
constexpr bool is_template_base_of<Base, Derived, 
  std::void_t<decltype(test<Base>(std::declval<Derived&>()))>> = true;

static_assert(is_template_base_of<Combination, MyCombination>);
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • Woah nice! It works perfectly! Would you know how to do it without C++20, I may be limited to C++17 or C++14. – AngelMixu Jul 19 '22 at 09:49