使用std :: function改变回调 [英] Alterantive for callbacks using std::function

查看:149
本文介绍了使用std :: function改变回调的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我正在尝试一个基本上如下的代码:

Currently I am trying out a code that does essentially the following:

void f(int x) { cout << "f("<<x<<")" << endl; }

class C
{
public:
   void m(int x) { cout << "C::m("<<x<<")" << endl; }
};

class C2
{
public:
   void registerCallback(function<void(int)> f)
   {
      v.push_back(f);
   }

private:
   vector<function<void(int)>> v;

   void callThem()
   {
      for (int i=0; i<v.size(); i++)
      {
         v[i](i);
      }
   }
};

int main()
{
   C2 registrar;

   C c;
   registrar.registerCallback(&f); // Static function
   registrar.registerCallback(bind(&C::m, &c, placeholders::_1)); // Method

   return 0;
}

但是我陷入了这种模式。我想检查一个回调是否已经注册,我想通过从向量中删除它可以取消注册一个回调。我刚刚了解到 std :: function 对象不能被比较,这意味着不可能在容器中搜索它们的存在。

This works pretty well. However I got stuck with this pattern. I would like to check if a callback already has been registered and I would like to be able to unregister a callback by removing it from the vector. I just learned that std::function objects can not be compared which means it is impossible to search for their existence in the container.

所以我需要一个替代。当然,我想保持编译时类型检查和注册任意类的方法的能力。

So I need an alternative. Of course I would like to keep compile-time type checks and the ability to register methods of arbitrary classes.

如何实现一个类似的解决方案,允许注销回调和检查双重注册?

How can I achieve a similar solution that allows unregistering the callback and checking for double registration? Are there any trade-offs I need to accept?

推荐答案

基本的问题是大多数函数对象是不可比的。虽然可以比较具有等式运算符的纯函数指针和用户定义函数对象,但是lambdas, std :: bind()等的结果不能。使用函数对象的地址来标识它们通常不是合适的方法,因为对象倾向于被复制。 可以使用 std :: reference_wrapper< Fun> 以避免复制它们,但是存储在 std :: function< F> 将仍然具有不同的地址。

The underlying problem is that most function objects are not comparable. While plain function pointers and user-defined function objects with an equality operator can be compared, lambdas, the result of std::bind(), etc. cannot. Using the address of function objects to identify them is generally not a suitable approach because the objects tend to be copied. It may be possible to use std::reference_wrapper<Fun> to avoid having them copied but the objects stored in a std::function<F> will still have a different address.

使用C ++ 11可变参数模板很容易创建 std :: function< ...> 的自定义版本,它提供比较功能。它甚至可以有两个版本:

With C++11 variadic templates it is reasonably easy to create a custom version of std::function<...> which provides comparison facilities. There may even be two versions thereof:


  • 一个版本,它接受任意函数对象,但显然只能比较类似的函数对象:是否使用构造函数参数提供相等运算符或不使用合适的基类。

  • 一个版本,总是提供一个工作比较,显然,不能用于非等同可比对象。

后者稍微容易定义,看起来像这样:

The latter is slightly easier to define and would look something like this:

template <typename...> class comparable_function;
template <typename RC, typename... Args>
class comparable_function<RC(Args...)> {
    struct base {
        virtual ~base() {}
        virtual RC    call(Args... args) = 0;
        virtual base* clone() const = 0;
        virtual bool  compare(base const*) const = 0;
    };
    template <typename Fun>
    struct concrete: base {
        Fun fun;
        concrete(Fun const& fun): fun(fun) {}
        RC call(Args... args) { return this->fun(args...); }
        base* clone() const { return new concrete<Fun>(this->fun); }
        bool compare(base const* other) const {
             concrete const* o = dynamic_cast<concrete<Fun>>(other);
             return o && this->fun == o->fun;
        }
    };
    std::unique_ptr<base> fun;
public:
    template <typename Fun>
    comparable_function(Fun fun): fun(new concrete<Fun>(fun)) {}
    comparable_function(comparable_function const& other): fun(other.fun->clone()) {}
    RC operator()(Args... args) { return this->fun->call(args); }
    bool operator== (comparable_function const& other) const {
        return this->fun->compare(other.fun.get());
    }
    bool operator!= (comparable_function const& other) { return !(this == other); }
};

我想,我忘了(和/或错误的)一些东西,但这是需要的。对于可选的可比较版本,您将有两个版本的 concrete :一个如上实现,另一个总是返回 false 。根据在构造函数中是否有 Fun 的运算符 == ,您可以创建一个或另一个。

I guess, I have forgotten (and/or mistyped) something but rather that's what's needed. For the optionally comparable version you'd have two versions of concrete: one which is implemented as above and another one which always returns false. Depending on whether there is an operator == for the Fun in the constructor you'd create one or the other.

这篇关于使用std :: function改变回调的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆