【问题标题】:What is the appropriate way to implement diverse callbacks?实现多样化回调的合适方式是什么?
【发布时间】:2023-02-24 19:24:26
【问题描述】:
class Base{
template<F>
void Foo(int x, int y, F callback) {

    int z, w;
    std::string str;
    /*

       do a lot of works get value of str, z and w

    */
    
    callback();
    /*
    or callback(z, w);
    or callback(str);
    */
}

int main() {

    Foo(1, 2, [](){
      std::cout << "no parameter";
    });

    Foo(1, 2, [](int z, int w){
      std::cout << z << w<< "two parameters";
    });

    Foo(1, 2, [](string str){
      std::cout << str.size() << "string parameter";
    });
}

本来我有几个几乎相同的函数,但是每个函数内部还是有一些差异无法合并。

我想通过传递不同的回调来处理这些差异,但是找不到统一的回调接口,因为差异不是很相似。

有没有优雅的方法来处理这种情况?谢谢你帮助我。

【问题讨论】:

  • 让你所有的回调都接受所有三个参数,即使它们不需要它们?真的,我们需要更多背景信息。删除了多余的标签。
  • 您使用模板 F 的方法允许任何数量的参数,包括没有。你需要决定Foo 真正做了什么,以及它需要传递什么参数(如果有的话)。如果你想在 Foo 函数中做不同的事情,那么你真的应该让不同的函数都做一件事情。如果函数之间有公共代码,则将它们放入不同的Foo变体可以调用的其他函数中。
  • 您是在问如何处理可能具有三个预定签名之一的回调吗?或者您是在询问如何调用可以具有任意数量的任意类型参数的函数?

标签: c++


【解决方案1】:

您可以通过 std::is_invocable_vstd::invocable 概念进行类型相关的编译时分派。这是您的通话示例。

    if constexpr (std::is_invocable_v<F>) {
      callback();
    } else if constexpr (std::is_invocable_v<F,int,int>) {
      callback(z,w);
    } else if constexpr (std::is_invocable_v<F,std::string>) {
      callback(str);
    }

这是一个demo,可完美转发可调用对象。


请注意,通过更改顺序,您可以选择哪个可调用对象获得优先权,以防 functoid 可通过不同方式调用,例如:

struct Callable {
  void operator()(); // <- preferred above because of order
  void operator()(std::string);
};

【讨论】:

    【解决方案2】:

    一种只需对代码进行少量更改的简单方法是使所有回调具有相同的签名。

    请注意,您的代码有很多拼写错误,而且并非所有内容都清楚。我假设 Foo 具有要传递给回调的参数。如果他们来自外部Foo,他们应该在将回调传递给Foo之前绑定到回调。

    #include <string>
    #include <iostream>
    
    template<typename F> void Foo(F callback) { callback(0,0,{}); }
    
    int main() {
    
        Foo([](int,int,const std::string&){
          std::cout << "no parameter";
        });
    
        Foo([](int z,int w,const std::string&){
          std::cout << z << w<< "two parameters";
        });
    
        Foo([](int,int,const std::string& str){
          std::cout << str.size() << "string parameter";
        });
    }
    

    Live Demo

    如评论中所述,当签名中的回调差异太大时,您应该重新考虑首先通过单个 Foo 处理它们是否有意义。如果回调确实具有不同的签名,如何继续取决于细节。下一个最简单的方法是使用不同的 Foo 重载,每个处理具有固定签名的回调。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-20
      • 2014-10-15
      • 2018-06-15
      • 2021-11-24
      • 1970-01-01
      • 2020-03-11
      相关资源
      最近更新 更多