开发者

Passing Derived Class Instances as void* to Generic Callbacks in C++

开发者 https://www.devze.com 2022-12-28 02:48 出处:网络
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.

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开发者_如何学Go 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.


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.


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);


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号