Reputation: 2627
This code distills the problem to its essence:
Infrastructure classes:
struct EventReceiverBase {
virtual ~EventReceiverBase() { }
};
template<typename T>
struct EventReceiver : public virtual EventReceiverBase {
virtual void receiveEvent(T* pSender) = 0;
};
struct EventSender {
EventReceiverBase* pReceiver;
template<typename T>
void sendEvent(T* pSender) {
EventReceiver<T>* pCastedReceiver =
dynamic_cast<EventReceiver<T>*>(pReceiver);
// HERE IS THE PROBLEM
// pCastedReceiver is null. T is BaseSender. The pointer
// being casted is really of type EventReceiver<DerivedSender>*,
// but it tries to cast to EventReceiver<BaseSender>*, and that
// fails.
pCastedReceiver->receiveEvent(pSender);
}
};
User classes:
struct BaseSender : public virtual EventSender {
void f() {
sendEvent(this);
}
};
struct DerivedSender : public BaseSender { };
struct MyClass : public virtual EventReceiver<DerivedSender> {
void receiveEvent(DerivedSender* pSender) { }
};
int main() {
MyClass my;
DerivedSender derivedSender;
derivedSender.pReceiver = &my;
derivedSender.f();
}
Can I recast this problem (no pun intended) to avoid this issue? I want to keep the user classes as simple as possible, while exposing the event sending and receiving functionality as close to this way as possible.
For example, I can "fix" it by making MyClass derive from EventReceiver<BaseSender> as well, but I'd really like to avoid that, as it would mean extra work in every class that receives events.
Edit: Executable paste: http://liveworkspace.org/code/4bm6OU$13
Upvotes: 1
Views: 586
Reputation: 726599
The reason why you see a problem has to do with the placement of the f
function in the BaseSender
: in the call to sendEvent
below, this
represents a pointer to BaseSender
. Essentially, the call below
void f() {
sendEvent(this);
}
is a short way to write this:
void f() {
sendEvent<BaseSender>(this);
}
Moving f
to DerivedSender
fixes this problem.
Another alternative is making BaseSender::f
a template:
template <typename T>
void f() {
sendEvent<T>((T*)this);
}
and calling it like this:
derivedSender.f<DerivedSender>();
This does not look particularly nice, but may be a work-around to code copy-pasting in situations when the real-world f
is large.
Yet another solution is to make BaseSender
a template, like this:
template<typename T>
struct BaseSender : public virtual EventSender {
void f() {
sendEvent((T*)this);
}
};
struct DerivedSender : public BaseSender<DerivedSender> {
};
This also works (link to ideone).
Upvotes: 1
Reputation: 129374
This shouldn'e be a problem. pSender will point to the orignal object, unless you are doing something really weird.
However, your receiveEvent in MyClass should take a BaseSender *
object, you'll then have to cast it to DerivedSender (or better yet, only use virtual methods defined in the base - this is how you SHOULD do things in general)
Upvotes: 0