【发布时间】:2021-03-26 00:14:51
【问题描述】:
我可以对回调函数进行类型转换吗?
我喜欢写一个注册和调用某些回调函数的库。
int MyCaller::add_callback_function(int (*callback_function)(), byte behaviour) {
this->callback = callback_function;
}
void MyCaller::run() {
int result = this->callback();
}
// ....
int get_number() { return 42; }
MyCaller my_caller;
my_caller.add_callback_function(get_number, 0);
my_caller.run();
不过,我还想支持输出布尔值的回调函数。
bool get_bool() { return true; }
MyCaller my_caller;
my_caller.add_callback_function(get_number, 0);
my_caller.run();
如果我尝试这个,我的代码似乎运行良好,但 gcc 会给出 warning: invalid conversion from 'bool (*)()' to 'int (*)()' [-fpermissive]。
为避免出现此警告,我重载了我的 add_callback_function,签名略有不同:
void MyCaller::add_callback_function(bool (*callback_function)(), byte behaviour) {
// ...
}
但是,从现在开始我就迷路了。我希望我可以将任何布尔值转换为整数,并调用原始方法:
void MyCaller::add_callback_function(bool (*callback_function)(), byte behaviour) {
this->add_callback_function(static_cast<int (*)()>(callback_function), byte behaviour);
}
但是,这会给出error: invalid static_cast from type 'bool (*)()' to type 'int (*)()'。
所以我好像做错了什么。
有没有干净的方法来做到这一点?
奖励要点:我的代码存储了一组回调函数,并且我希望能够选择性地添加上下文参数(例如,允许调用函数之外的方法)。我计划将所有这些回调(具有不同的签名)存储为int (*)(),并在调用它们之前将它们转换为正确的形式。这是最好的方法,还是有其他(可能更好)的方法来处理这个问题?
我能想到的替代方案是只支持一个非常具体的回调签名,但这会迫使用户编写包装器。我喜欢把这个负担放在图书馆身上。
【问题讨论】:
-
这是一个可怕的想法(即使它是可能的)。如果您不小心,将不同的签名强制为单个签名是破坏调用堆栈的好方法。如果您有使用不同签名的回调,则将它们存储在适当类型的单独数组中。
-
@RemyLebeau 所以你显然不建议走这条路。你会推荐什么?只允许一个回调签名?仅供参考,现实情况稍微复杂一些。我的原始代码接受了一个返回布尔值的回调。我现在想扩展我的代码以接受 int,但要向后兼容。
-
"只允许一个回调签名?" - 基本上,是的。这是唯一理智要做的事情。回调的调用者 (
run()) 需要知道使用哪个签名来调用每个回调,除非您准备根据与每个回调关联的一些跟踪数据将每个回调类型转换为不同的签名,否则它只处理 1 个签名要容易得多。那么如果用户需要为不同的签名使用包装器怎么办——这就是 lambdas 的好处。