在C ++ 11(如夹杂的所做的更改std::bind
),是否有实现,而不依赖于任何外部的核心语言或标准库简单单线程观察者模式(如推荐的方式boost::signal
)?
编辑
如果有人可以张贴展示了如何在依赖一些代码boost::signal
可以用新的语言功能降低,仍然是非常有用的。
在C ++ 11(如夹杂的所做的更改std::bind
),是否有实现,而不依赖于任何外部的核心语言或标准库简单单线程观察者模式(如推荐的方式boost::signal
)?
编辑
如果有人可以张贴展示了如何在依赖一些代码boost::signal
可以用新的语言功能降低,仍然是非常有用的。
我认为, bind
使得它更容易地创建插槽(CFR了。 “优选”的语法与在“便携”语法 -这一切要离开)。 观察者管理,但是,是不是变得越来越复杂。
但随着@R。 Martinho费尔南德斯提到:一个std::vector<std::function< r(a1) > >
现在容易没有麻烦为(人工)“纯虚”接口类创建的。
根据要求:在连接管理的想法 - 也许完全错误的,但你会得到的想法:
// note that the Func parameter is something
// like std::function< void(int,int) > or whatever, greatly simplified
// by the C++11 standard
template<typename Func>
struct signal {
typedef int Key; //
Key nextKey;
std::map<Key,Func> connections;
// note that connection management is the same in C++03 or C++11
// (until a better idea arises)
template<typename FuncLike>
Key connect( FuncLike f ) {
Key k=nextKey++;
connections[k]=f;
return k;
}
void disconnect(Key k){
connections.erase(k);
}
// note: variadic template syntax to be reviewed
// (not the main focus of this post)
template<typename Args...>
typename Func::return_value call(Args... args){
// supposing no subcription changes within call:
for(auto &connection: connections){
(*connection.second)(std::forward(...args));
}
}
};
用法:
signal<function<void(int,int)>> xychanged;
void dump(int x, int y) { cout << x << ", " << y << endl; }
struct XY { int x, y; } xy;
auto dumpkey=xychanged.connect(dump);
auto lambdakey=xychanged.connect([&xy](int x, int y){ xy.x=x; xy.y=y; });
xychanged.call(1,2);
既然你要求的代码,我的博客文章一个C ++ 11信号系统的性能包含基于没有进一步依赖C ++ 11功能的全功能信号系统的单文件执行(虽然单线程的,这是性能要求)。
下面是一个简单的使用例子:
Signal<void (std::string, int)> sig2;
sig2() += [] (std::string msg, int d) { /* handler logic */ };
sig2.emit ("string arg", 17);
更多的例子可在此发现的单元测试 。
我写我自己的重量轻的信号/槽类,其返回连接句柄。 现有的答案的关键系统处于异常的脸很脆弱。 你必须要格外小心删除的东西有一个显式调用。 我更喜欢使用RAII的开/关对。
一个显着的缺乏在我的库支持是得到你的电话返回值的能力。 我认为提高::信号具有计算总的返回值的方法。 在实践中通常你不需要这个,我只是觉得混乱,但我可以拿出作为未来的运动乐趣这样的回报方法。
我的类的一个很酷的事情是在插槽和SlotRegister类。 SlotRegister提供了一个公共接口,你可以安全地连接到专用插槽。 这可以防止调用你的observer方法的外部对象。 这很简单,但很好的封装。
我不相信我的但代码是线程安全的。
//"MIT License + do not delete this comment" - M2tM : http://michaelhamilton.com
#ifndef __MV_SIGNAL_H__
#define __MV_SIGNAL_H__
#include <memory>
#include <utility>
#include <functional>
#include <vector>
#include <set>
#include "Utility/scopeGuard.hpp"
namespace MV {
template <typename T>
class Signal {
public:
typedef std::function<T> FunctionType;
typedef std::shared_ptr<Signal<T>> SharedType;
static std::shared_ptr< Signal<T> > make(std::function<T> a_callback){
return std::shared_ptr< Signal<T> >(new Signal<T>(a_callback, ++uniqueId));
}
template <class ...Arg>
void notify(Arg... a_parameters){
if(!isBlocked){
callback(std::forward<Arg>(a_parameters)...);
}
}
template <class ...Arg>
void operator()(Arg... a_parameters){
if(!isBlocked){
callback(std::forward<Arg>(a_parameters)...);
}
}
void block(){
isBlocked = true;
}
void unblock(){
isBlocked = false;
}
bool blocked() const{
return isBlocked;
}
//For sorting and comparison (removal/avoiding duplicates)
bool operator<(const Signal<T>& a_rhs){
return id < a_rhs.id;
}
bool operator>(const Signal<T>& a_rhs){
return id > a_rhs.id;
}
bool operator==(const Signal<T>& a_rhs){
return id == a_rhs.id;
}
bool operator!=(const Signal<T>& a_rhs){
return id != a_rhs.id;
}
private:
Signal(std::function<T> a_callback, long long a_id):
id(a_id),
callback(a_callback),
isBlocked(false){
}
bool isBlocked;
std::function< T > callback;
long long id;
static long long uniqueId;
};
template <typename T>
long long Signal<T>::uniqueId = 0;
template <typename T>
class Slot {
public:
typedef std::function<T> FunctionType;
typedef Signal<T> SignalType;
typedef std::shared_ptr<Signal<T>> SharedSignalType;
//No protection against duplicates.
std::shared_ptr<Signal<T>> connect(std::function<T> a_callback){
if(observerLimit == std::numeric_limits<size_t>::max() || cullDeadObservers() < observerLimit){
auto signal = Signal<T>::make(a_callback);
observers.insert(signal);
return signal;
} else{
return nullptr;
}
}
//Duplicate Signals will not be added. If std::function ever becomes comparable this can all be much safer.
bool connect(std::shared_ptr<Signal<T>> a_value){
if(observerLimit == std::numeric_limits<size_t>::max() || cullDeadObservers() < observerLimit){
observers.insert(a_value);
return true;
}else{
return false;
}
}
void disconnect(std::shared_ptr<Signal<T>> a_value){
if(!inCall){
observers.erase(a_value);
} else{
disconnectQueue.push_back(a_value);
}
}
template <typename ...Arg>
void operator()(Arg... a_parameters){
inCall = true;
SCOPE_EXIT{
inCall = false;
for(auto& i : disconnectQueue){
observers.erase(i);
}
disconnectQueue.clear();
};
for (auto i = observers.begin(); i != observers.end();) {
if (i->expired()) {
observers.erase(i++);
} else {
auto next = i;
++next;
i->lock()->notify(std::forward<Arg>(a_parameters)...);
i = next;
}
}
}
void setObserverLimit(size_t a_newLimit){
observerLimit = a_newLimit;
}
void clearObserverLimit(){
observerLimit = std::numeric_limits<size_t>::max();
}
int getObserverLimit(){
return observerLimit;
}
size_t cullDeadObservers(){
for(auto i = observers.begin(); i != observers.end();) {
if(i->expired()) {
observers.erase(i++);
}
}
return observers.size();
}
private:
std::set< std::weak_ptr< Signal<T> >, std::owner_less<std::weak_ptr<Signal<T>>> > observers;
size_t observerLimit = std::numeric_limits<size_t>::max();
bool inCall = false;
std::vector< std::shared_ptr<Signal<T>> > disconnectQueue;
};
//Can be used as a public SlotRegister member for connecting slots to a private Slot member.
//In this way you won't have to write forwarding connect/disconnect boilerplate for your classes.
template <typename T>
class SlotRegister {
public:
typedef std::function<T> FunctionType;
typedef Signal<T> SignalType;
typedef std::shared_ptr<Signal<T>> SharedSignalType;
SlotRegister(Slot<T> &a_slot) :
slot(a_slot){
}
//no protection against duplicates
std::shared_ptr<Signal<T>> connect(std::function<T> a_callback){
return slot.connect(a_callback);
}
//duplicate shared_ptr's will not be added
bool connect(std::shared_ptr<Signal<T>> a_value){
return slot.connect(a_value);
}
void disconnect(std::shared_ptr<Signal<T>> a_value){
slot.disconnect(a_value);
}
private:
Slot<T> &slot;
};
}
#endif
Supplimental scopeGuard.hpp:
#ifndef _MV_SCOPEGUARD_H_
#define _MV_SCOPEGUARD_H_
//Lifted from Alexandrescu's ScopeGuard11 talk.
namespace MV {
template <typename Fun>
class ScopeGuard {
Fun f_;
bool active_;
public:
ScopeGuard(Fun f)
: f_(std::move(f))
, active_(true) {
}
~ScopeGuard() { if(active_) f_(); }
void dismiss() { active_ = false; }
ScopeGuard() = delete;
ScopeGuard(const ScopeGuard&) = delete;
ScopeGuard& operator=(const ScopeGuard&) = delete;
ScopeGuard(ScopeGuard&& rhs)
: f_(std::move(rhs.f_))
, active_(rhs.active_) {
rhs.dismiss();
}
};
template<typename Fun>
ScopeGuard<Fun> scopeGuard(Fun f){
return ScopeGuard<Fun>(std::move(f));
}
namespace ScopeMacroSupport {
enum class ScopeGuardOnExit {};
template <typename Fun>
MV::ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun&& fn) {
return MV::ScopeGuard<Fun>(std::forward<Fun>(fn));
}
}
#define SCOPE_EXIT \
auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE) \
= MV::ScopeMacroSupport::ScopeGuardOnExit() + [&]()
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#ifdef __COUNTER__
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str, __COUNTER__)
#else
#define ANONYMOUS_VARIABLE(str) \
CONCATENATE(str, __LINE__)
#endif
}
#endif
一个示例应用利用我的图书馆:
#include <iostream>
#include <string>
#include "signal.hpp"
class Observed {
private:
//Note: This is private to ensure not just anyone can spawn a signal
MV::Slot<void (int)> onChangeSlot;
public:
typedef MV::Slot<void (int)>::SharedSignalType ChangeEventSignal;
//SlotRegister is public, users can hook up signals to onChange with this value.
MV::SlotRegister<void (int)> onChange;
Observed():
onChange(onChangeSlot){ //Here is where the binding occurs
}
void change(int newValue){
onChangeSlot(newValue);
}
};
class Observer{
public:
Observer(std::string a_name, Observed &a_observed){
connection = a_observed.onChange.connect([=](int value){
std::cout << a_name << " caught changed value: " << value << std::endl;
});
}
private:
Observed::ChangeEventSignal connection;
};
int main(){
Observed observed;
Observer observer1("o[1]", observed);
{
Observer observer2("o[2]", observed);
observed.change(1);
}
observed.change(2);
}
上述的输出将是:
o[1] caught changed value: 1
o[2] caught changed value: 1
o[1] caught changed value: 2
正如你所看到的,槽自动断开死的信号。
我在此也有一展身手自己。 我的努力可以在这个要点,这将继续发展,被发现。 。 。
https://gist.github.com/4172757
我使用的是不同的风格,更类似于变更通知中JUCE比BOOST信号。 连接管理使用一些lambda语法,通过复制做一些拍摄完成。 这是迄今为止运作良好。