Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine, at compile time, whether virtual method has been overridden

Suppose I have some class A:

// a.h:
class A {
virtual unsigned foo() {
   return 0;
}

Then, I have many classes (let's call these intermediates) inheriting from A (class B, class C...etc). These classes will NEVER override foo().

// b.h:
class B : public A {
bool override_foo = true;

    // ....
}

// c.h:
class C : public A {
    bool override_foo = false;
}

Then, I have many classes inheriting from the intermediates. Class D, E, etc. Only one if these will ever inherit from a single intermediate, i.e, these is a 1 to 1 relationship between these classes and the intermediates. There is guaranteed to only (and always) be a single one of these classes inheriting from one of the intermediates.

Now, these classes MAY override foo. However, if they do, and the intermediate class they inherit from does not define override_foo as true, I need compilation to fail.

So, for example, if we have class D:

// d.h:
class D : public B {
    unsigned foo() override();
}

Compilation would proceed fine, since B has override_foo defined as true.

However, we this should fail:

// e.h:
class E : public C {
    unsigned foo() override();
}

Since C has override_foo as false.

Is this something that is achievable? Unfortunately there is very little flexibility in this problem. I work for a company where the intermediate classes, (B, C) can be changed very slightly. They are created from the compilation of a proprietary framework. I could add a member to them, or any sort of macro magic. Same for the base class A. However, the grandchildren classes, D,E, are defined by other engineers to override parts of the framework, and I need to prevent them from doing so unless the intermediate classes have some key attributes.

Thanks!

like image 947
fhowy Avatar asked Dec 09 '25 08:12

fhowy


2 Answers

You can use final keyword to disallow inherited classes to override the method.

class A {
public:
  virtual ~A() = default;
  virtual void foo() {}
};

class B : public A {
public:
  void foo() final { A::foo(); }
};

class C : public A {};

class D : public B {
public:
  void foo() override;  // <- compilation error here
};

class E : public C {
public:
  void foo() override;
};

int main() {}
like image 64
Tarek Dakhran Avatar answered Dec 10 '25 20:12

Tarek Dakhran


The other answers require your intermediate classes to override the base class version of foo() with final so the derived class can't override it instead of making a boolean member variable determine the result - and that would work. And maybe it is what you want, but it's not what you said you wanted. So I am going to answer the question you asked even though the other answers might be more what you were actually after.

If you can modify B then this makes the derived classes pretty simple to write:

#include <stdio.h>
#include <type_traits>

struct B {
    virtual int foo() {
        puts("B");
    };

    template<typename X>
    using baseFooType = typename std::enable_if<X::override_foo,decltype(((B*)nullptr)->foo())>::type;
};

struct C : public B {
    constexpr static bool override_foo = true;
};

struct D : public B {
    constexpr static bool override_foo = true;
};

struct E : public C {
    baseFooType<C> foo() override {
        puts("E");
    }
};

struct F : public D {
    baseFooType<D> foo() override {
        puts("F");
    }
};

int main()
{
    E().foo();
    F().foo();
}

Try it here: https://onlinegdb.com/rJe5jXQhHI

If you can't modiby B then you can use a helper class like this:

#include <stdio.h>
#include <type_traits>

struct B {
    virtual int foo() {
        puts("B");
    };
};

template<typename X>
struct baseFooType {
    using type = typename std::enable_if<X::override_foo,decltype(((B*)nullptr)->foo())>::type;
};

struct C : public B {
    constexpr static bool override_foo = true;
};

struct D : public B {
    constexpr static bool override_foo = true;
};

struct E : public C {
    baseFooType<C>::type foo() override {
        puts("E");
    }
};

struct F : public D {
    baseFooType<D>::type foo() override {
        puts("F");
    }
};

int main()
{
    E().foo();
    F().foo();
}

Try it here: https://onlinegdb.com/BJXg4QhB8

Note: I used constexpr in the example because I thought it would need that to be passed as a template parameter but it turns out it isn't needed - you can just use const instead.

like image 32
Jerry Jeremiah Avatar answered Dec 10 '25 20:12

Jerry Jeremiah



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!