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 /