17

Within a C++ class hierarchy, is it possible to enforce a requirement that a particular virtual function always call its base class's implementation also? (Like the way constructors chain?)

I'm looking at a case where a deep class hierarchy has some common interface functions which each child overrides. I'd like each derived class' override to chain through to the base class. It's straightforward to do this explicitly with eg the code below, but there's the risk that someone implementing a new derived class might forget to chain through to the base.

Is there some pattern to enforce this, such that the compiler will throw an error if an override fails to chain the base?

So, in

class CAA 
{
   virtual void OnEvent( CEvent *e ) { 
     // do base implementation stuff;
   }
}

class CBB : public CAA
{
   typedef CAA BaseClass;
   virtual void OnEvent( CEvent *e ) { 
       DoCustomCBBStuff();
       BaseClass::OnEvent( e ); // chain to base
   }
}

class CCC : public CBB
{
   typedef CBB BaseClass;
   virtual void OnEvent( CEvent *e ) { 
       Frobble();
       Glorp();
       BaseClass::OnEvent( e ); // chain to CBB which chains to CAA, etc
   }
}

class CDD : public CCC
{
   typedef CCC BaseClass;
   virtual void OnEvent( CEvent *e ) { 
       Meep();
       // oops! forgot to chain to base!
   }
}

is there a way, some template trick or syntactic gimmick, to make CDD throw a more obvious error?

Crashworks
  • 40,496
  • 12
  • 101
  • 170
  • 1
    Not to my knowledge, but you could always give the baseclass a non-virtual function and store a list of function pointers, to which each derived class adds its part. – Kerrek SB Jan 03 '12 at 00:06
  • Effectively a duplicate of [How to force child same virtual function call its parent virtual function first](http://stackoverflow.com/questions/5644338/how-to-force-child-same-virtual-function-call-its-parent-virtual-function-first) – James McNellis Jan 03 '12 at 00:49
  • possible duplicate of [C++ : Call a base class method automatically?](http://stackoverflow.com/questions/3107974/c-call-a-base-class-method-automatically) – outis Apr 22 '12 at 00:10

6 Answers6

9

The way its done is the base class method is not virtual and calls a protected virtual method.

Of course, that only handles one level.

In your particular situation, a large amount of infrastructure can make it work, but it's not worth it.

The typical response is to add a comment

// Always call base class method
Joshua
  • 40,822
  • 8
  • 72
  • 132
3

Put a special 'hidden' type in the Base class with a private constructor, using friend to ensure that only the Base can create it. This code on ideone.

If there are multiple levels, this unfortunately does not guarantee that the immediate base class is called. Hence struct E : public D; could implement E::foo() with a call to B:: foo when you might prefer a call to D::foo().

struct Base {
        struct Hidden {
                friend class Base;
                private:
                        Hidden() {}
        };
        virtual Hidden foo() {
                cout << "Base" << endl;
                return Hidden(); // this can create a Hidden
        }
};

struct D : public B {
        virtual Hidden foo() {
                cout << "D" << endl;
                // return Hidden(); // error as the constructor is private from here
                return B :: foo();
        }
};

If you tried to implement D :: foo() without a return or with return Hidden(), you will get an error message. The only way to compile this is to use return B :: foo().

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • Cuty... but slightly flawed. `foo()` should return a const reference and you should prevent copies of `Hidden` from being made. Of course, your solution is already good to prevent accidental omission, especially renaming `Hidden` to `DoNotForgetToChain` or something like that. – Matthieu M. Jan 03 '12 at 07:16
3

Following a simple rule to derive through a template class it is possible.

#include <iostream>

struct TEvent
{
};

struct Base {
    virtual void CallOnEvent(TEvent * e)
    {
        OnEvent(e);
    }
    virtual void OnEvent(TEvent * e)
    {
        std::cout << "Base::Event" << std::endl;
    }
    void CallUp(TEvent * e)
    {
    }

};

template <typename B>
struct TDerived : public B
{
    void CallUp( TEvent * e )
    {
        B::CallUp(e);
        B::OnEvent(e);
    }
    virtual void CallOnEvent( TEvent * e )
    {
        CallUp(e);
        this->OnEvent(e);
    }
};

struct Derived01 : public TDerived< Base >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived01::Event" << std::endl;
    }
};

struct Derived02 : public TDerived< Derived01 >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived02::Event" << std::endl;
    }
};

struct Derived03 : public TDerived< Derived02 >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived03::Event" << std::endl;
    }
};

struct Derived04 : public TDerived< Derived03 >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived04::Event" << std::endl;
    }
};


int main( void )
{
 Derived04 lD4;
 lD4.CallOnEvent(0);
 return 0;
}

This code yields (codepad):

Base::Event
Derived01::Event
Derived02::Event
Derived03::Event
Derived04::Event

Regarding some answers using typeid. I would never consider using typeid for anything else than debugging. This is due to two things:

  • dynamic type checking can be done in a much more efficient ways (without creating type_info object i.e. using dynamic_cast, some methods
  • C++ standard basically guarantees only the existance of typeid, but not really anything regarding how it works (most things are "compiler specific")

edit:

A slightly more complex example with multiple inheritance. This one unfortunately is not solvable without explicit calls in classes that do inherit from multiple bases (mainly because it is not clear what should happen in such cases, so we have to explicitly define the behaviour).

#include <iostream>

struct TEvent
{
};

struct Base {
    virtual void CallOnEvent(TEvent * e)
    {
        OnEvent(e);
    }
    virtual void OnEvent(TEvent * e)
    {
        std::cout << "Base::Event" << std::endl;
    }

    void CallUp(TEvent * e)
    {
    }
};

template <typename B >
struct TDerived : public B
{
    void CallUp( TEvent * e )
    {
        B::CallUp(e);
        B::OnEvent(e);
    }
    virtual void CallOnEvent( TEvent * e )
    {
        CallUp(e);
        this->OnEvent(e);
    }
};

struct Derived01 : virtual public TDerived< Base >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived01::Event" << std::endl;
    }
};

struct Derived02 : virtual public TDerived< Derived01 >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived02::Event" << std::endl;
    }
};

typedef TDerived< Derived02 > TDerived02;
typedef TDerived< Derived01 > TDerived01;
struct Derived03 : virtual public TDerived02, virtual public TDerived01
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived03::Event" << std::endl;
    }

    virtual void CallOnEvent( TEvent * e )
    {
        CallUp(e);
        Derived03::OnEvent(e);
    }
    void CallUp( TEvent * e )
    {
        TDerived02::CallUp(e);
        TDerived01::CallUp(e);
    }
};

struct Derived04 : public TDerived< Derived03 >
{
    void OnEvent(TEvent * e)
    {
        std::cout << "Derived04::Event" << std::endl;
    }
};


int main( void )
{
 Derived04 lD4;
 Derived03 lD3;

 lD3.CallOnEvent( 0 );
 std::cout << std::endl;
 lD4.CallOnEvent( 0 );

 return ( 0 );
}

Result is (ideone):

Base::Event      \                  \
Derived01::Event | - from Derived02 |
Derived02::Event /                  |-- from Derived03
Base::Event      \__ from Derived01 |
Derived01::Event /                  |
Derived03::Event                    /

Base::Event      \                  \                  \
Derived01::Event | - from Derived02 |                  |
Derived02::Event /                  |-- from Derived03 |-- from Derived04
Base::Event      \__ from Derived01 |                  |
Derived01::Event /                  |                  |
Derived03::Event                    /                  |
Derived04::Event                                       /
elmo
  • 1,189
  • 1
  • 10
  • 35
2

There is no support for this in the C++ language, but expanding on KerrekSB's comment, you could do something like this:

class A {
public:
    void DoEvent(int i) {
        for (auto event = events.begin(); event != events.end(); ++event)
            (this->*(*event))(i);
    }

protected:
    typedef void (A::*Event)(int);

    A(Event e) {
        events.push_back(&A::OnEvent);
        events.push_back(e);
    }

    void OnEvent(int i) {
        cout << "A::OnEvent " << i << endl;
    }

    vector<Event> events;
};

class B : public A {
public:
    B() : A((Event)&B::OnEvent) { }

protected:
    B(Event e) : A((Event)&B::OnEvent) {
        events.push_back(e);
    }

    void OnEvent(int i) {
        cout << "B::OnEvent " << i << endl;
    }
};

class C : public B {
public:
    C() : B((Event)&C::OnEvent) { }

protected:
    C(Event e) : B((Event)&C::OnEvent) {
        events.push_back(e);
    }

    void OnEvent(int i) {
        cout << "C::OnEvent " << i << endl;
    }
};

Then use it like this

int main() {
    A* ba = new B;
    ba->DoEvent(32);

    B* bb = new B;
    bb->DoEvent(212);

    A* ca = new C;
    ca->DoEvent(44212);

    B* cb = new C;
    cb->DoEvent(2);

    C* cc = new C;
    cc->DoEvent(9);
}

That outputs

A::OnEvent 32
B::OnEvent 32

A::OnEvent 212
B::OnEvent 212

A::OnEvent 44212
B::OnEvent 44212
C::OnEvent 44212

A::OnEvent 2
B::OnEvent 2
C::OnEvent 2

A::OnEvent 9
B::OnEvent 9
C::OnEvent 9

You have to do a little work, but you don't have to manually call the baser member function at the end of each call. Here is the live demo.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
0

There's a clang-tidy check for this: https://reviews.llvm.org/rCTE329448 (made by me, so you may address me any questions on it)

Zinovy Nis
  • 455
  • 6
  • 9
0

There isn't anything directly enforcing an overriding function to do anything in particular, except returning a certain type. However, if you make the base class virtual function private no function can call it on the base class but derived classes can override it. You then also provide a public function which calls the virtual function as well as a function doing the base class logic. The logic from the base class probably should go into a separate function (possibly the non-virtual forwarding function directly) to avoid executing it twice if the object actually happens to be a base object.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • The problem with this is that it only covers one level - namely, that all derived calls will call the ultimate base function, but all intermediate classes do not participate in that party. – Xeo Jan 03 '12 at 00:09
  • @Xeo: agreed. I'm not aware of any approach which enforces chaining of virtual functions. Although I that the idea of chaining overriding function is cute (it is used e.g. in Xt, an object oriented GUI system for X11 implemented in C; all the object oriented stuff needed to be done manually), I didn't need for anything but construction and destruction which happen to be chained. – Dietmar Kühl Jan 03 '12 at 00:13