好的,所以我已经将此作为一个工作示例,但我只想尝试清理它。
目前我有一个订阅风格的事件系统,带有回调例程。
将常规函数和类成员函数添加到存储变量中没有问题。
但是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));
}
现在只需要对参数列表进行泛化。
答案 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。