2010-04-21 67 views
1

这是一个涉及到的问题,所以我会尽我所能解释发生了什么。如果我错过了什么,请告诉我,这样我可以澄清。将派生类实例作为void *传递给C++中的泛型回调

我们有一个回调系统,一方面模块或应用程序提供“服务”,客户端可以使用此服务执行操作(基本上是非常基本的IPC)。对于未来的参考,让我们说我们有一些定义,像这样:

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 

这对于基本类型,比如结构或建宏工作正常。

我们有一个MI结构有点像这样:

Device <- Disk <- MyDiskProvider 

class Disk : public virtual Device 
class MyDiskProvider : public Disk 

提供者可能是从硬件驱动程序什么一点胶水,处理磁盘映像。关键是类继承了磁盘。

我们有一个“服务”,这是通知系统中的所有新的磁盘,这是哪里的东西解开:

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! 
} 

的问题是,SomeDiskProvider继承磁盘,但回调处理程序不会收到该类型(因为回调函数指针必须是通用的)。

RTTI和模板可以在这里帮助吗?

任何建议将不胜感激。

+0

为什么不更像'typedef int(* callback)(Device *);'? – Earlz 2010-04-21 04:59:06

+0

回调函数还需要接受没有Device作为基类的对象 - 例如结构体或内建类型(int,float等)。 – 2010-04-21 05:00:57

+0

@Matt所以我认为做一个'CallbackMessage'类来包装这些东西是不实际的? – Earlz 2010-04-21 05:02:30

回答

1

虽然我不是100%确定,我认为static_cast从T *到void *并返回到原始指针类型T *应该工作。正如我所看到的那样,问题是reinterpret_cast只改变指针的类型而不改变它的实际值,在继承的情况下static_cast应该调整一个指针,使它指向对象的开始。

免责声明:你在这里语言的黑暗面,我不能保证它可以工作。

+0

'static_cast'将起作用。在这种情况下'dynamic_cast'会更好。 – 2010-04-21 05:26:02

+0

本,虽然我非常确定dynamic_cast无效*作品我不相信它可以用另一种方式 - 我记得过去有过这样的问题。这就是我没有提到它的原因。 – Tomek 2010-04-21 05:32:44

+0

我不能回到原来的类型T *,因为回调从来不知道它是什么类。然而,我可以回到在回调中的Disk *,这很像R Samuel Klatchko的回答。 – 2010-04-21 07:33:19

1

您的代码看起来有风险。问题是你投SomeDiskProvider *void *,然后把它回到Disk *

你应该回到你施放的确切类型。所以,你可以先投你SomeDiskProvider *Disk *

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

你施放回SomeDiskProvider *

SomeDiskProvider *pDisk = reinterpret_cast<SomeDiskProvider*>(p); 
+0

由于回调是通用的,我无法将其重新转换为SomeDiskProvider *,并且期望在磁盘上工作。尽管如此,将它们转换成磁盘*然后将*那个*传递给回调确实很有意义。然后回调可以回退到磁盘*并执行所需的操作。 – 2010-04-21 07:34:10

1

reinterpret_cast<>是确定的,如果你从some_type *转换为void *,然后再返回。但是,您说您正在使用多重继承,在这种情况下,层次结构中的一个类中的this可能与另一个类中的this的值不同; dynamic_cast<>被设计为走继承树并给你正确的答案。

所以,在SomeDiskProvider使用两个转换:

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

回调是完全按照你在你的问题给大家看。

相关问题