views:

141

answers:

3

This is a bit of an involved problem, so I'll do the best I can to explain what's going on. If I miss something, please tell me so I can clarify.

We have a callback system where on one side a module or application provides a "Service" and clients can perform actions with this Service (A very rudimentary IPC, basically). For future reference let's say we have some definitions like so:

typedef int (*callback)(void*); // This is NOT in our code, but makes explaining easier.

installCallback(string serviceName, callback cb); // Really handled by a proper management system

sendMessage(string serviceName, void* arg); // arg = value to pass to callback

This works fine for basic types such as structs or builtins.

We have an MI structure a bit like this:

Device <- Disk <- MyDiskProvider

class Disk : public virtual Device
class MyDiskProvider : public Disk

The provider may be anything from a hardware driver to a bit of glue that handles disk images. The point is that classes inherit Disk.

We have a "service" which is to be notified of all new Disks in the system, and this is where things unravel:

void diskHandler(void *p)
{
    Disk *pDisk = reinterpret_cast<Disk*>(p); // Uh oh!

    // Remainder is not important
}

SomeDiskProvider::initialise()
{
    // Probe hardware, whatever...

    // Tell the disk system we're here!
    sendMessage("disk-handler", reinterpret_cast<void*>(this)); // Uh oh!
}

The problem is, SomeDiskProvider inherits Disk, but the callback handler can't receive that type (as the callback function pointer must be generic).

Could RTTI and templates help here?

Any suggestions would be greatly appreciated.

+1  A: 

While I am not 100% sure I think static_cast from T* to void* and back to the original pointer type T* should work. As I see it the problem is that reinterpret_cast only changes the type of pointer without altering its actual value and static_cast in case of inheritance should adjust a pointer so that it points to the beginning of the object.

DISCLAIMER: You are on the dark side of the language here and I cannot guarantee it will work at all.

Tomek
`static_cast` will work. `dynamic_cast` would be even better in this scenario.
Ben Voigt
Ben, While I am pretty sure dynamic_cast to void* works I am not convinced it works the other way around - I remember having problems with that in the past. This is why I haven't mentioned it.
Tomek
I can't go back to the original type T* as the callback never knows what class it is. I can however go back to, in the example, Disk* in the callback - much like R Samuel Klatchko's answer.
Matthew Iselin
@Mathew, if you know that the void * pointer will always be obtained from specific class or one of its derivatives then you do as in the answer you mentioned: static_cast to common parent (Disk *) and then static_cast to void *. Then you can static_cast void * back to Disk *. The drawback (or not - depends how you look at it) is that you need to think about you hierarchy and you probably need to make all the methods used directly in callback virtual.I would still do some research on static_cast versus dynamic_cast as you are using multiple inheritance.
Tomek
@Tomek, Yep, all the methods in the callback are virtual. I've done a little more research into static_cast and dynamic_cast and more importantly, into multiple inheritance itself as defined by the specification. Thanks for the help :)
Matthew Iselin
+1  A: 

Your code looks risky. The problem is that you cast a SomeDiskProvider * to void * and then cast it back to Disk *.

You should cast back to the exact type you cast from. So you could first cast your SomeDiskProvider * to a Disk *:

reinterpret_cast<void*>(static_cast<Disk *>(this))

or you cast back to a SomeDiskProvider *:

SomeDiskProvider *pDisk = reinterpret_cast<SomeDiskProvider*>(p);
R Samuel Klatchko
I can't cast back into a SomeDiskProvider* as the callback is generic, and expects to work on a Disk. Casting into a Disk* and then passing *that* to the callback does make a lot of sense, though. Then the callback can cast back to a Disk* and perform the actions it needs to.
Matthew Iselin
+1  A: 

reinterpret_cast<> is OK if you're converting from some_type * to void * and back again. However, you said that you're using multiple inheritance, in which case this in one class in your hierarchy may not have the same value as this in another class; dynamic_cast<> is designed to walk the inheritance tree and give you the right answer.

So, in SomeDiskProvider use both casts:

SomeDiskProvider::initialise()
{
    // Gets the right value for 'this' for the Disk part of SomeDiskProvider.
    Disk *pDisk = dynamic_cast<Disk *>(this);

    // OK, since the callback converts it back to Disk *
    sendMessage("disk-handler", reinterpret_cast<void*>(this));
}

The callback is exactly as you showed it in your question.

Niall C.