现代C++中的类型安全回调系统

4

我正在开发一个使用回调系统的模块,但该系统的实现不是很好。客户端通过注册一个ID来接收回调,并将获得一个变量(或两个变量或没有变量)。问题在于几乎每个ID所对应的变量都是不同的。(例如:Id1 -> char*,Id2 -> int)。这是通过指针传递变量来实现的。因此,回调看起来像下面这样:

typedef void (*NotifFunctionPtr)(void* ctx, const void* option);

这种方法存在许多问题,我希望用一种类型安全且现代化的方式来处理它。然而,这并不像看起来那么简单,我有一些想法(如使用boost :: function或将void *替换为封装类型和ptr的结构体),但我认为可能有更好的想法,所以我想知道C ++中设置类型安全回调的现代方式是什么。
编辑:另一个想法是通过调用相同类型T的模板函数来注册具有类型T的回调。这可行吗?是否在库中实现?
3个回答

10
你的问题不在于回调函数,而是你想要将所有回调函数视为相同类型,但它们的签名是不同的。因此,你必须使用肮脏的 C void* 技巧或者如果你想要使用类型安全方法,你就需要为此付出代价,并提供不同的方法来注册不同类型的回调函数——这是正确的方式,个人认为。

解决了这个问题后,你可以使用signalssignals2库,或者自己实现一个轮子,使用function作为基础(以避免重新编写类型抹除)。

4
还有一种选择是用 boost::any 替换空指针。我强烈建议使用 boostsigc++ 分离信号,而不是使用 ID,但如果进行这么多重构不可行,boost::any 至少可以运行时检查你是否获得了正确的类型。 - Jan Hudec
2
@JanHudec:是的,没错,boost::any将在运行时提供类型安全的解决方案,也就是说,如果您传递了错误类型的参数,它不会编译失败,但会在执行时失败。仍然比void*和/或联合要好得多,因为它们会在未能匹配类型时悄悄地导致UB。 - David Rodríguez - dribeas
关于一个模板函数,它可以使用类型 T 注册回调并以相同的类型 T 进行回调,您怎么看? - cprogrammer
@cprogrammer:我在考虑超出单个函数调用的回调,这种情况下您提议的问题是如何存储它们(需要类型擦除),同时能够以类型安全的方式知道稍后传递什么参数。 - David Rodríguez - dribeas
是的,这不是我所要求的类型安全,但垃圾将在回调类内部。这并不一定是一个好主意。 - cprogrammer
@cprogrammer:模板不会有太大帮助。调用回调的代码提供了特定类型。您需要在注册时知道该类型(信号选项;您可以使用模板来允许分配接受不同但兼容类型的函数),或者在运行时检查它(boost::any)。 - Jan Hudec

3

boost::function在这里是一个恰当的选择。你可以获得函数对象的类型安全性,而不需要太多改变代码。


2

我不知道这是否解决了使用不同数量参数的问题。 - cprogrammer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接