Ashkan
Ashkan

Reputation: 1085

Make sure a class does not inherit from two interfaces

I have this problem. There is a framework that is developed and used by us. We have a few interfaces that the user of this framework needs to implement. I want to make sure that two of these interfaces can never be implemented on the same class. here is an example

class A{
  void a() = 0;
};
class B{
  void b() = 0;
};

class C: public A, public B { // This should give error
};
class D: public A{ // OK
};

So far I thought maybe I can use static_assert and std::is_convertible but I can't put my hands on how to do it.

Edit: I don't know who is going to write the derived classes so I want to have it in the base interfaces if possible. Basically assume that I don't have access to derived classes because they are not in our code base.

Thanks.

Upvotes: 1

Views: 104

Answers (1)

Georgii Firsov
Georgii Firsov

Reputation: 306

Write own trait like this:

template<typename Derived, typename... BaseCandidates>
struct has_no_base;

template<typename Derived>
struct has_no_base<Derived>
    : std::true_type { };

template<typename Derived, typename BaseFirst, typename... BasesRest>
struct has_no_base<Derived, BaseFirst, BasesRest...>
    : std::conditional_t<
        std::is_base_of<BaseFirst, Derived>,
        std::true_type,
        has_no_base<Derived, BasesRest...>
      > { };

template<typename Derived, typename... PossibleBases>
struct has_only_one_base;

template<typename Derived>
struct has_only_one_base<Derived>
    : std::false_type { };

template<typename Derived, typename Base>
struct has_only_one_base<Derived, Base>
    : std::conditional_t<
        std::is_base_of_v<Base, Derived>,
        std::true_type,
        std::false_type
      > { };

template<typename Derived, typename BaseFirst, typename... BasesRest>
struct has_only_one_base<Derived, BaseFirst, BasesRest...>
    : std::conditional_t<
        std::is_base_of_v<BaseFirst, Derived>,
        has_no_base<Derived, BasesRest...>,
        has_only_one_base<Derived, BasesRest...>
      > { };

And use it like that:

class MyClass : public A, public B
{
    static_assert(has_only_one_base<MyClass, A, B>::value, "Error");
};

Moreover, as far as you are a developer of this framework, you can provide some check trait:

template<typename Type>
using is_valid_class = has_only_one_base<Type, IFirst, ISecond, ... ILast>;

And use it:

class MyClass : public A, public B
{
    static_assert(is_valid_class<MyClass>::value, "Error");
};

Upvotes: 2

Related Questions