【问题标题】:Typecast callback functions类型转换回调函数
【发布时间】: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 的好处。

标签: c++ casting callback


【解决方案1】:

您可以将一种函数指针类型转换为另一种,只有在将其转换为 real 函数指针类型时才能调用它。您只能将它们存储为错误的签名。

以任何其他方式调用它们会导致未定义的行为。最可能的症状取决于您的平台当前的调用约定。但实际上,不要这样做。

最简单的正确解决方案是存储一个

std::function<int()>

这不是函数指针,但可以存储任何可以复制、移动和调用零参数的回调(包括函数指针),并返回与 int 兼容的内容(bool 符合条件作为)。

std::function 也可以存储有状态的对象。例如:

struct Foo {
  int count = 0;
  int callback(){ return ++count; }
};

Foo foo;
std::function<int()> f = [&foo]{ return foo.callback(); };

你自然而然地要对一生负责。查找“C++ lambdas”以了解上述语法。

【讨论】:

  • 谢谢。我担心我必须重新考虑一些设计。这实际上是针对 Arduino 的一个爱好项目。不幸的是,逻辑是有状态的,我发现我自己复制了轮询某些事情的代码(检查输入、闪烁的灯、处理异常),每个 sn-p 都有不同的签名。所以我写了这个小库。我怀疑std::function 是该平台的正确方法(如果它完全受支持的话)。所以我担心我会回到原点,在多个地方复制这些轮询器。或者想到一个常见的回调签名,并编写很多包装器。
  • @macfreek lambdas 可用于包装函数,如果不是指针的话; +[]()-&gt;int{return boolfunc();} 是指向调用 boolfunc 的 int 返回函数的函数指针。还滚动你自己的分配免费标准函数是 150 行左右的代码。
猜你喜欢
  • 2014-04-10
  • 1970-01-01
  • 2012-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-24
相关资源
最近更新 更多