在调用之前检查std :: function的有效性?

时间:2016-10-07 12:44:32

标签: c++ c++11

我试图编写一个简单但灵活的事件系统(大多数情况下只是作为练习,我知道现有的库有非常好的事件处理程序),而且我遇到了一个小绊脚石。

如果成员函数的对象在调用之前仍然存在,那么如何检查std::function委托(可能通过lambda,可能是std :: bind)是否为有效函数?我尝试过使用std::function's bool运算符,但没有取得任何成功。

理想情况下,我希望A.在委托函数内部以外的位置进行检查,并且当正在检查的std :: function不是&时,代码仍然有效。 ta delegate。

有什么想法吗?

编辑:这是我跑的测试的来源

#include <iostream>
#include <string>
#include <functional>

class Obj {
public:
    std::string foo;
    Obj(std::string foo) : foo(foo) {}
    std::function<void()> getDelegate() {
        auto callback = [this]() {this->delegatedFn();};
        return callback;
    }
    void delegatedFn() {
        std::cout << foo << std::endl;
    }
};

int main() {
    Obj* obj = new Obj("bar");
    std::function<void()> callback = obj->getDelegate();
    callback();
    delete obj;

    //perform some type of check here whether function is valid, without needing to know whether the function is a delegate or not
    if(callback) {
        std::cout << "Callback is valid" << std::endl; //callback is still considered valid after obj is deleted
        callback(); //no exception thrown, prints a random series of characters
    }
    else {
        std::cout << "Callback is invalid" << std::endl;
    }

    return 0;
}

2 个答案:

答案 0 :(得分:4)

您可以使用智能指针(std::shared_ptr / std::weak_ptr)而不是裸指针:

#include <iostream>
#include <string>
#include <functional>
#include <memory>

class Obj {
public:
    std::string foo;
    Obj(std::string foo) : foo(foo) {}
    void delegatedFn() {
        std::cout << foo << std::endl;
    }
};

int main() {
    auto obj = std::make_shared<Obj>("bar");
    std::weak_ptr<Obj> ptr = obj;
    std::function<void()> callback = [ptr](){
        auto sh = ptr.lock();
        if(sh) { std::cout << "valid" << std::endl; sh->delegatedFn(); }
        else { std::cout << "invalid" << std::endl; }
    };
    callback();
    obj = nullptr;
    callback(); 
    return 0;
}

在这种情况下,您不是直接检查std::function有效性(当您为其指定某些内容时有效,即使某些内容捕获了悬空指针) 相反,您可以在函数本身内检查引用的对象是否仍然存在。

答案 1 :(得分:2)

我使用的广播/监听模式如下所示:

template<class...Args>
struct broadcaster {
  std::vector< std::weak_ptr< std::function<void(Args...)> > > callbacks;

  void operator()(Args...args) const {
    std::remove_erase_if( begin(callbacks), end(callbacks), [](auto&& ptr){return !ptr;} );
    auto tmp = callbacks;
    for (auto pf : tmp) {
      if (pf && *pf) (*pf)(args...);
    }
  }
  std::shared_ptr<void> listen( std::shared_ptr<std::function<void(Args...)>> f ) {
    callbacks.push_back(f);
    return f;
  }

  std::shared_ptr<void> listen( std::function<void(Args...)> f ) {
    auto ptr = std::make_shared<std::function<void(Args...)>>(std::move(f));
    return listen(ptr);
  }
};

使用.listen收听回复邮件broadcaster。他们返回shared_ptr<void>令牌。

只要存在该令牌,广播公司就会在传入的功能对象上发送消息。

Obj会存储std::vector<std::shared_ptr<void>> tokens或单std::shared_ptr<void>。当它被销毁时,它的听众会自动注销。

或者,Obj可以从shared_from_this继承。然后它实现

std::function<void()> delegate;
std::shared_ptr<std::function<void()>> getDelegatedFn() {
  if (!delegate) delegate = [this]{ this->delegateFn(); }
  return {
    &delegate,
    shared_from_this()
  };
}

,它共享Obj实例本身的生命周期(使用shared_ptr的别名构造函数)。将此传递给listen并完成。