在函数调用中使用bind

时间:2017-09-26 17:47:19

标签: c++ callback std-function

好的,所以我已经将此作为一个工作示例,但我只想尝试清理它。

目前我有一个订阅风格的事件系统,带有回调例程。

将常规函数和类成员函数添加到存储变量中没有问题。

但是id喜欢通过重载我的Event.subscribe方法来清理它。

目前它确实如此:

template <class T>
class EventObject
{
public:
    void subscribe(const T& arg)
    {
        this->events.push_back(arg);
    }
std::vector<T> events;
}

然后在我的主要使用它是这样的:

void someCallback(const std::string& line)
{
    cout <<line;
}

typedef  std::function<void(const std::string& line)> onMessageCallbackFunction;
EventObject<onMessageCallbackFunction> messageCallback;

messageCallback.subscribe(someCallback);

现在,当我使用Class成员函数时,pickle会出现。我讨厌在传递函数之前必须对函数进行内在绑定。它使代码看起来很脏。

例如:

class B
{
    void callbk(const std::string& line) {}
};

B b;
using std::placeholders::_1;
onMessageCallbackFunction memfunc = std::bind(&B::callbk, b, _1);
messageCallback.subscribe(memfunc);

现在它确实有效,但看起来很糟糕。什么是语法,所以我可以预先形成:

messageCallback.subscribe(&B::callbk, b);

尝试了一些,但似乎无法让它正确。

不是什么:

template <class J>
void subscribe(J::T cls,T& t); //Nope.
void subscribe(J&& cls,T& t); //Nope.

超级关闭:( thx tobi)

template< class J, class... Args >
void subscribe(void(J::*funct)(Args), J& inst) {
    using std::placeholders::_1;
    subscribe(std::bind(funct, inst, _1));
}

现在只需要对参数列表进行泛化。

2 个答案:

答案 0 :(得分:1)

我的建议是使用lambda(std::bind在它出现的那天就已经过时了,因为lambda在几乎所有方面都是优越的。)

单独拼出lambda永远不会伤害。这样做没有性能成本。编译器将忽略不必要的副本。

我还会使用std :: function来存储信号并从兼容的传入函数类型转换。

您的呼叫站点代码看起来像这样,我认为您同意的是干净和富有表现力的:

EventObject<void(std::string)> messageCallback;
B b;

auto handler = [&b](std::string const& line)
{
    b.callbk(line);
};

messageCallback.subscribe(handler);

完整示例:

#include <string>
#include <vector>
#include <functional>
#include <type_traits>

template<class Sig> struct EventObject;

template<class R, class...Args>
struct EventObject<R (Args...)>
{
    using slot_type = std::function<R(Args...)>;

    template
    <
        class F,
        typename std::enable_if
        <
            std::is_constructible
            <
                slot_type, 
                typename std::decay<F>::type
            >::value
        >::type * = nullptr
    >
    void subscribe(F&& f)
    {
        subscribers_.emplace_back(std::forward<F>(f));
    }

    std::vector<slot_type> subscribers_;
};

class B
{
public:
    void callbk(const std::string& line) {}
};

int main()
{
    EventObject<void(std::string)> messageCallback;
    B b;

    auto handler = [&b](std::string const& line)
    {
        b.callbk(line);
    };

    messageCallback.subscribe(handler);
}

答案 1 :(得分:0)

  

语法是什么,所以我可以预先形成:

messageCallback.subscribe(&B::callbk, b);

您的界面已经允许传递任何可调用对象,并且没有理由使其更复杂。 EventObject不应该知道回调是自由函数还是其他函数。我完全同意std::bind是乏味的,看起来并不好看,我宁愿使用lambda:

EventObject<onMessageCallbackFunction> messageCallback;
B b;
messageCallback.subscribe([&b](const std::string& line){return b.callbk(line);});
PS:我不确定我是否理解你使用tempalte的动机。看来你只想在onMessageCallbackFunction s中使用它,这样只需存储它们的矢量即可。

PPS:为了完整起见,您可以在方法中隐藏bind

#include <vector>
#include <string>
#include <iostream>
#include <functional>
using std::placeholders::_1;

template <class T>
class EventObject {
public:
    void subscribe(const T& arg)
    {
        this->events.push_back(arg);
    }
    template<typename C>
    void subscribe(void(C::*mem_fun)(const std::string&),C& c){
        subscribe(std::bind(mem_fun,c,_1));
    }       
    std::vector<T> events;
};

typedef  std::function<void(const std::string& line)> onMessageCallbackFunction;

struct B {
    void callbk(const std::string& line) {}
};

int main(){
    EventObject<onMessageCallbackFunction> messageCallback;
    B b;
    messageCallback.subscribe(&B::callbk,b);
}

这仅适用于返回void并返回string的成员函数,但我甚至不愿意将其设为通用而只使用lambdas。