Universal Callback template class with pointer to method callback - c++
Inspired by another article here on SO (C++ callback using class member) I tried to write a universal CallbackHandler.
CallbackHandler.hpp
#pragma once
#include <functional>
template <typename CallbackClass, typename CallbackArgType>
class CallbackHandler
{
public:
std::function<void(CallbackArgType ct)> m_callbackFunc;
CallbackHandler(CallbackClass * handler, std::function<void(CallbackArgType)> method)
{
//m_callbackFunc is supposed to stand for a member to pointer callback function with one
//parameter of any type
m_callbackFunc = std::bind(method, handler, std::placeholders::_1);
}
};
#include "wrapper_T.cpp"
I want to use it in several other templated namespaces/classes like here:
wrapper.hpp
//this wrappers main purpose is to combine the constructor of a non templated class (MyModule)
//and hold a (global) callback method for it (m_parentCallback)
namespace wrapper
{
extern std::function<void(wxImage *)> m_parentCallback;
template<typename ParentClass>
MyModule GetNewModule(ParentClass* parent, void (ParentClass::* method)(wxImage *));
}
wrapper.cpp
namespace wrapper
{
//This is only to avoid multiple definition error - actual definition is in wrapper_T.cpp
std::function<void(wxImage *)> m_parentCallback;
}
wrapper_T.cpp
namespace wrapper
{
template<typename ParentClass>
MyModule GetNewModule(ParentClass* parent, void (ParentClass::* method)(wxImage *))
{
//the callback type of this wrapper/class is wxImage*
std::shared_ptr<CallbackHandler<ParentClass, wxImage*>> handler =
std::make_shared< CallbackHandler<ParentClass, wxImage*>>(parent, method);
//EDIT - SOLVED: <- Error C2664: Cant convert argument 2 from "void (__thiscall MyModule::*)(void)" to "std::function<void(wxImage*)>"
m_parentCallback = std::bind(&CallbackHandler<ParentClass, wxImage*>::m_callbackFunc, handler, std::placeholders::_1);
//<- Error C2679: no suitable binary operator "=" found
return std::make_unique<MyModule>();
}
}
I wanted to use the callback like this:
MyModule.cpp
wrapper::m_parentCallback(&img);
I want to initialize the whole thing like this:
MainClass.cpp
MainClass::MainClass()
{
//declared in header: std::unique_ptr<MyModule> module
module = std::move(wrapper::GetNewModule(this, &MainClass::CallbackFunc));
}
void MainClass::CallbackFunc(wxImage * img)
{ /* do something with it */ }
I have the class with the "this" pointer and the pointer to method "CallbackFunc", which should be alright.
But I dont see how to use my CallbackHandler class for a std::function callback pointer.
Or did I overdo it with the wrapper holding a pointer to a method of the CallbackHandler, which holds a pointer to a method of the actual callback method?
All of this is no design choice, I just want the CallbackHandler to be portable and working, while having an interface which is easy to use.
EDIT:
I tried to apply the comments suggestions on the code, but I was to fast
with claiming the first problem was solved. The error was just hidden by the next error. If I try to compile with just this line:
std::shared_ptr<CallbackHandler<ParentClass, wxImage*>> handler =
std::make_shared< CallbackHandler<ParentClass, wxImage*>>(parent, method);
//<- Error C2664: "CallbackHandler<ParentClass,wxImage *>::
//CallbackHandler(CallbackHandler<ParentClass,wxImage *> &&)"
//: converting argument 2 from "void (__thiscall MainClass::* )(wxImage *)"
//to "std::function<void (wxImage *)>" not possible
// with
// [
// ParentClass=MainClass
]
//(freely translated into english by me)
So, the missing argument was not the only problem.
If std::bind on methods (member functions) does not work, I do have to change the CallbackClass as well, dont I?
Maybe something along those lines:
std::function<void(CallbackArgType cat)> m_callbackFunc;
CallbackHandler(CallbackClass * handler, std::function<void(CallbackArgType)> method)
{
//m_callbackFunc = std::bind(method, handler, std::placeholders::_1);
m_callbackFunc = [method](auto img) { method(img); };
}
Replace
m_parentCallback = std::bind(&CallbackHandler<ParentClass, wxImage*>::m_callbackFunc, handler, std::placeholders::_1);
with
m_parentCallback = [handler](auto img){ handler->m_parentCallback(img); };
I don't think bind was designed to work with member functor objects on top of everything. But lambdas handle that seamlessly.
I noticed that my problem is beeing solved by every event handler, calling an event function. So I used an event handler as basis vor my CallbackHandler. I found and used this event handler, as there are most certainly many other good written examples:
https://www.codeproject.com/Articles/1256352/CppEvent-How-to-Implement-Events-using-Standard-Cp#holdingHandlerFunction
I just wrapped it in a class and it is a little overkill for my purpose, but it works. All credits to Shmuel Zang (see link above)
EventHandler.hpp
#ifndef EventHandler_hpp
#define EventHandler_hpp
// https://www.codeproject.com/Articles/1256352/CppEvent-How-to-Implement-Events-using-Standard-Cp#holdingHandlerFunction
#include <functional>
#include <list>
#include <algorithm>
#include <utility>
#include <atomic>
#include <mutex>
#include <future>
namespace EventHandler
{
template <typename... Args> class EventHandler_Base
{
public:
typedef std::function<void(Args...)> handler_func_type;
typedef unsigned int handler_id_type;
explicit EventHandler_Base(const handler_func_type& handlerFunc)
: m_handlerFunc(handlerFunc)
{
m_handlerId = ++m_handlerIdCounter;
}
// copy constructor
EventHandler_Base(const EventHandler_Base& src)
: m_handlerFunc(src.m_handlerFunc), m_handlerId(src.m_handlerId)
{
}
// move constructor
EventHandler_Base(EventHandler_Base&& src)
: m_handlerFunc(std::move(src.m_handlerFunc)), m_handlerId(src.m_handlerId)
{
}
// copy assignment operator
EventHandler_Base& operator=(const EventHandler_Base& src)
{
m_handlerFunc = src.m_handlerFunc;
m_handlerId = src.m_handlerId;
return *this;
}
// move assignment operator
EventHandler_Base& operator=(EventHandler_Base&& src)
{
std::swap(m_handlerFunc, src.m_handlerFunc);
m_handlerId = src.m_handlerId;
return *this;
}
// function call operator
void operator()(Args... params) const
{
if (m_handlerFunc)
{
m_handlerFunc(params...);
}
}
bool operator==(const EventHandler_Base& other) const
{
return m_handlerId == other.m_handlerId;
}
operator bool() const
{
return m_handlerFunc;
}
handler_id_type id() const
{
return m_handlerId;
}
private:
handler_func_type m_handlerFunc;
handler_id_type m_handlerId;
static std::atomic_uint m_handlerIdCounter;
};
template <typename... Args> std::atomic_uint EventHandler_Base<Args...>::m_handlerIdCounter(0);
template <typename... Args> class Event_Base
{
public:
typedef EventHandler_Base<Args...> handler_type;
Event_Base()
{
}
// copy constructor
Event_Base(const Event_Base& src)
{
std::lock_guard<std::mutex> lock(src.m_handlersLocker);
m_handlers = src.m_handlers;
}
// move constructor
Event_Base(Event_Base&& src)
{
std::lock_guard<std::mutex> lock(src.m_handlersLocker);
m_handlers = std::move(src.m_handlers);
}
// copy assignment operator
Event_Base& operator=(const Event_Base& src)
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
std::lock_guard<std::mutex> lock2(src.m_handlersLocker);
m_handlers = src.m_handlers;
return *this;
}
// move assignment operator
Event_Base& operator=(Event_Base&& src)
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
std::lock_guard<std::mutex> lock2(src.m_handlersLocker);
std::swap(m_handlers, src.m_handlers);
return *this;
}
typename handler_type::handler_id_type add(const handler_type& handler)
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
m_handlers.push_back(handler);
return handler.id();
}
inline typename handler_type::handler_id_type add(const typename handler_type::handler_func_type& handler)
{
return add(handler_type(handler));
}
bool remove(const handler_type& handler)
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
auto it = std::find(m_handlers.begin(), m_handlers.end(), handler);
if (it != m_handlers.end())
{
m_handlers.erase(it);
return true;
}
return false;
}
bool remove_id(const typename handler_type::handler_id_type& handlerId)
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
auto it = std::find_if(m_handlers.begin(), m_handlers.end(),
[handlerId](const handler_type& handler) { return handler.id() == handlerId; });
if (it != m_handlers.end())
{
m_handlers.erase(it);
return true;
}
return false;
}
void call(Args... params) const
{
handler_collection_type handlersCopy = get_handlers_copy();
call_impl(handlersCopy, params...);
}
std::future<void> call_async(Args... params) const
{
return std::async(std::launch::async, [this](Args... asyncParams) { call(asyncParams...); }, params...);
}
inline void operator()(Args... params) const
{
call(params...);
}
inline typename handler_type::handler_id_type operator+=(const handler_type& handler)
{
return add(handler);
}
inline typename handler_type::handler_id_type operator+=(const typename handler_type::handler_func_type& handler)
{
return add(handler);
}
inline bool operator-=(const handler_type& handler)
{
return remove(handler);
}
protected:
typedef std::list<handler_type> handler_collection_type;
void call_impl(const handler_collection_type& handlers, Args... params) const
{
for (const auto& handler : handlers)
{
handler(params...);
}
}
handler_collection_type get_handlers_copy() const
{
std::lock_guard<std::mutex> lock(m_handlersLocker);
// Since the function return value is by copy,
// before the function returns (and destruct the lock_guard object),
// it creates a copy of the m_handlers container.
return m_handlers;
}
private:
handler_collection_type m_handlers;
mutable std::mutex m_handlersLocker;
};
}
#endif // EventHandler_hpp
I then use this generic class to save a callback member method:
CallbackHandler.hpp
#ifndef CallbackHandler_hpp
#define CallbackHandler_hpp
#pragma once
#include "EventHandler.hpp"
#include <functional>
#include <mutex>
using namespace EventHandler;
template <typename... Args>
class CallbackHandler
{
public:
typedef std::function<void(Args...)> callbackFunc_type;
explicit CallbackHandler() : mb_callbackIsSet(false), mi_handlerID(0){}
explicit CallbackHandler(const callbackFunc_type& handlerFunc) : mb_callbackIsSet(false), mi_handlerID(0)
{
SetCallbackMethod(handlerFunc);
}
~CallbackHandler()
{
m_callbackEvent.remove_id(mi_handlerID);
}
void SetCallbackMethod(const callbackFunc_type& handlerFunc)
{
m_callbackEvent.remove_id(mi_handlerID);
mi_handlerID = m_callbackEvent.add([=](Args... params) {
handlerFunc(params...);
});
mb_callbackIsSet = true;
}
bool DoCallback(Args... params)
{
if (mb_callbackIsSet)
{
std::lock_guard<std::mutex> lock(callbackLocker);
m_callbackEvent(params...);
return true;
}
return false;
}
private:
unsigned int mi_handlerID;
Event_Base<Args...> m_callbackEvent;
bool mb_callbackIsSet;
std::mutex callbackLocker;
};
#endif //CallbackHandler_hpp
I can use the CallbackHandler in every other class now:
//Imagine an example class named Screenshotmodul
//In this example I use wxImage as a callback object, it is the output of my Screenshotmodul class and should be given back to my main class
//It could be any other (or several others like this: <ObjectType, OtherCallbackType> )
class Screenshotmodul
{
public:
//[...]
CallbackHandler<wxImage> m_callbackHandler;
template<typename ParentClass>
void SetCallbackMethod(ParentClass* parent, void (ParentClass::* method)(wxImage))
{
//using the given object pointer and the given member method in a lambda function
//saving that lambda as callback method
m_callbackHandler.SetCallbackMethod([=](wxImage img) {
(parent->*method)(img); });
}
}
In my main class I can then set a callback method:
std::unique_ptr<Screenshotmodul> sm = std::make_unique<Screenshotmodul>();
sm->SetCallbackMethod(this, &MyMainClass::CallbackfuncForScreenshotmodul);
//Of course there should be a callback function as just described:
void MyMainClass::CallbackfuncForScreenshotmodul(wxImage img)
{
//img now contains the callback value, that the Screenshotmodul class created
}
This is my approach and it is not perfect, but it works for me.
Cheers
Natu
Related
"-Werror=subobject-linkage" for lambda as a template parameter in header: what is the reason to reject this?
A follow-up for this question. Given this code in header.hxx: template <class Func> class ScopeGuard { public: /** #param func function object to be executed in dtor */ explicit ScopeGuard( Func && func ) : m_func( std::move(func) ) {} ~ScopeGuard() { if (m_bDismissed) return; m_func(); } /** Dismisses the scope guard, i.e. the function won't be executed. */ void dismiss() { m_bDismissed = true; } private: // noncopyable until we have good reasons... ScopeGuard(const ScopeGuard&) = delete; ScopeGuard& operator=(const ScopeGuard&) = delete; Func m_func; bool m_bDismissed = false; }; // Get functor for cleanup to use in FlagRestorationGuard auto GetFlagRestorationGuard(bool& i_flagRef) { return [&i_flagRef, resetVal = i_flagRef] { i_flagRef = resetVal; }; } class FlagRestorationGuard : public ScopeGuard<decltype(GetFlagRestorationGuard(std::declval<bool&>()))> { public: FlagRestorationGuard( bool& i_flagRef, bool i_temporaryValue ) : ScopeGuard(GetFlagRestorationGuard(i_flagRef)) { i_flagRef = i_temporaryValue; } }; and this code in file.cxx: #include <cassert> #include "header.hxx" void foo() { bool bFlag = false; { // Test that comphelper::FlagRestorationGuard properly sets and resets the flag comphelper::FlagRestorationGuard aGuard(bFlag, true); assert(bFlag); } assert(!bFlag); } produces this error: error: ‘comphelper::FlagRestorationGuard’ has a field ‘comphelper::FlagRestorationGuard::<anonymous>’ whose type uses the anonymous namespace [-Werror=subobject-linkage] class FlagRestorationGuard ^~~~~~~~~~~~~~~~~~~~ I see how the lambda is of course unnamed object, using an implicit unnamed namespace; but how is this relevant in this case? Why should this diagnostic block anything here? Reference to the code. Reference to error.
Using auto specifier in std::function and lambdas
I have a class named Handler wich stores some lambdas. What I want to do is to have a std::vector of std::function that stores all my events, for exemple. I really can't figure out why lambdas doesn't work as I expected. Here's the handler.h: class Handler { public: Handler(); ~Handler(); void Register(const char* outcome, std::function<auto()> lambda); void Trigger(const char* outcome); private: std::vector<int> identifier; std::vector<char*> outcome; std::vector<std::function<auto()>> func; }; And handler.cpp: Handler::Handler() { //ctor stuff here } Handler::~Handler() { this->func.clear(); this->outcome.clear(); this->identifier.clear(); //... } void Handler::Register(const char* outcome, std::function<auto()> lambda) { static int identifier = 0; identifier++; this->outcome.push_back((char*)outcome); this->identifier.push_back(identifier); this->func.push_back(lambda); //Sort outcome } void Handler::Trigger(const char * outcome) { int i; for (i = 0; i < this->identifier.size(); i++) { if (!strcmp(outcome, this->outcome.at(i))) break; } this->func[i](); } However, if I specify lambdas in a Handler::Register it wont let me throwing no suitable user-defined conversion from "lambda []void ()->void" to "std::function<auto()> exists. In this example I use void return type but other types also error, I don't understant why can't the template from std::function deduce it out, if it is what's happening. Handler* events = new Handler(); events->Register("Up", [=]() -> void { //Error here! //do stuff //return something? }); Is there any other way to do this, like without overloading Handler::Register?
auto is not a type, so std::function<auto()> is not a type either. From how you are using it, std::function<void()> is probably what you want. There are other problems with your code, as noted in the comments, so I would change Handler to this class Handler { public: Handler(); // default ~Handler is fine void Register(std::string outcome, std::function<void()> lambda); void Trigger(const std::string & outcome outcome) const; void Trigger(std::size_t index) const; private: using Outcomes = std::map<std::string, std::function<void()>/*, custom string comparator ?*/>; std::vector<Outcomes::iterator> identifier; Outcomes outcomes; }; void Handler::Register(std::string outcome, std::function<void()> func) { auto emplaced = outcomes.emplace(std::move(outcome), std::move(func)); identifier.push_back(emplaced.first); } void Handler::Trigger(const std::string & outcome) const { outcomes.at(outcome)(); } void Handler::Trigger(std::size_t index) const { identifier[index]->second(); }
how to ( pascal : procedure of object ) in c++
I want to pass a class function as a parameter of C function in Pascal. It is achieved with the keyword (procedure of object) so the compiler will take care of 'this' parameter. But it seems complicated in c++. #include <stdio.h> typedef void (*func)(void); class Class{ public: void sub(void) { printf("Foo"); } }; void test(func f) { f(); } int main() { Class c; test(c.sub); }
You'll need the function to take a generic function type, either making it a template: template <typename F> void test(F f) { f(); } or using type erasure: #include <functional> void test(std::function<void()> f) { f(); } Then use either std::bind or a lambda to bind the member function to the object: test(std::bind(&Class::sub, &c)); test([&]{c.sub();});
I have been searching for similar issue. There is an answer from someone zayats80888 (thank you). #include <functional> using TEventHandler = std::function<void(int)>; class Button { TEventHandler onClick; public: Button(){ onClick = NULL;}; void Run() { if (onClick) onClick(42); }; }; class Page { Button Bnt1; void ClickHandler(int Arg){/*do smth by Btn1->onClick*/}; Page() { Btn1 = new Button(); Btn1->onClick = [this](int arg) { goToCountersPage(arg); }; } } Page Page1; void main() { Page1 = new Page(); // }
c++11 : Compare lambda expression
Imagine have the following class : #include <functional> #include <vector> template<typename T1> class Signaler { public: typedef std::function<void (T1)> Func; public: Signaler() { } void Call(T1 arg) { for(Int32 i = (Int32)_handlers.size() - 1; i > -1; i--) { Func handler = _handlers[i]; handler(arg); } } Signaler& operator+=(Func f) { _handlers.push_back( f ); return *this; } Signaler& operator-=(Func f) { for(auto i = _handlers.begin(); i != _handlers.end(); i++) { if ( (*i).template target<void (T1)>() == f.template target<void (T1)>() ) { _handlers.erase( i ); break; } } return *this; } private: std::vector<Func> _handlers; }; And I use it the following way : Signaler Global::Signal_SelectionChanged; class C1 { public: void Register() { Global::Signal_SelectionChanged += [&](SelectionChangedEventArgs* e) { this->selectionChangedEvent_cb(e); }; } void Unregister() { Global::Signal_SelectionChanged -= [&](SelectionChangedEventArgs* e) { this->selectionChangedEvent_cb(e); }; } void selectionChangedEvent_cb(SelectionChangedEventArgs* e) {} }; class C2 { public: void Register() { Global::Signal_SelectionChanged += [&](SelectionChangedEventArgs* e) { this->selectionChangedEvent_cb(e); }; } void Unregister() { Global::Signal_SelectionChanged -= [&](SelectionChangedEventArgs* e) { this->selectionChangedEvent_cb(e); }; } void selectionChangedEvent_cb(SelectionChangedEventArgs* e) {} }; Now, the problem that I have is when I call 'Unregister' from the class C2, it removes the wrong version of the 'lambda" expression, because the 'lambda' looks similar. How can I solve this problem ? Any idea ? Thanks
The problem is that you are using std::function::target with a type that is not the type of the object stored in the std::function, so it is returning a null pointer. That is, you need to know the actual type of the object stored in the std::function to be able to call target. Even if you were to call target with the lambda closure type used to add the callback, this wouldn't work for two reasons: first, lambda closure types are unique (5.1.2p3) so the += and -= lambdas have different types even if they are syntactically identical; second, the closure type for a lambda-expression is not defined to have an operator== (5.1.2p3-6, 19-20), so your code would not even compile. Switching from lambdas to std::bind wouldn't help, as bind types are also not defined to have operator==. Instead, consider using an id to register/unregister callbacks. You could also use your own functor which defines operator==, but that would be a lot of work.
C++11 observer pattern (signals, slots, events, change broadcaster/listener, or whatever you want to call it)
With the changes made in C++11 (such as the inclusion of std::bind), is there a recommended way to implement a simple single-threaded observer pattern without dependence on anything external to the core language or standard library (like boost::signal)? EDIT If someone could post some code showing how dependence on boost::signal could be reduced using new language features, that would still be very useful.
I think that bind makes it easier to create slots (cfr. the 'preferred' syntax vs. the 'portable' syntax - that's all going away). The observer management, however, is not becoming less complex. But as #R. Martinho Fernandes mentions: an std::vector<std::function< r(a1) > > is now easily created without the hassle for an (artificial) 'pure virtual' interface class. Upon request: an idea on connection management - probably full of bugs, but you'll get the idea: // 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)); } } }; Usage: 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);
Since you're asking for code, my blog entry Performance of a C++11 Signal System contains a single-file implementation of a fully functional signal system based on C++11 features without further dependencies (albeit single-threaded, which was a performance requirement). Here is a brief usage example: Signal<void (std::string, int)> sig2; sig2() += [] (std::string msg, int d) { /* handler logic */ }; sig2.emit ("string arg", 17); More examples can be found in this unit test.
I wrote my own light weight Signal/Slot classes which return connection handles. The existing answer's key system is pretty fragile in the face of exceptions. You have to be exceptionally careful about deleting things with an explicit call. I much prefer using RAII for open/close pairs. One notable lack of support in my library is the ability to get a return value from your calls. I believe boost::signal has methods of calculating the aggregate return values. In practice usually you don't need this and I just find it cluttering, but I may come up with such a return method for fun as an exercise in the future. One cool thing about my classes is the Slot and SlotRegister classes. SlotRegister provides a public interface which you can safely link to a private Slot. This protects against external objects calling your observer methods. It's simple, but nice encapsulation. I do not believe my code is thread safe, however. //"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 An example application making use of my library: #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); } Output of the above would be: o[1] caught changed value: 1 o[2] caught changed value: 1 o[1] caught changed value: 2 As you can see, the slot disconnects dead signals automatically.
Here's what I came up with. This assumes no need to aggregate results from the listeners of a broadcast signal. Also, the "slot" or Signal::Listener is the owner of the callback. This ought to live with the object that your (I'm guessing...) lambda is probably capturing so that when that object goes out of scope, so does the callback, which prevents it from being called anymore. You could use methods described in other answers as well to store the Listener owner objects in a way you can lookup. template <typename... FuncArgs> class Signal { using fp = std::function<void(FuncArgs...)>; std::forward_list<std::weak_ptr<fp> > registeredListeners; public: using Listener = std::shared_ptr<fp>; Listener add(const std::function<void(FuncArgs...)> &cb) { // passing by address, until copy is made in the Listener as owner. Listener result(std::make_shared<fp>(cb)); registeredListeners.push_front(result); return result; } void raise(FuncArgs... args) { registeredListeners.remove_if([&args...](std::weak_ptr<fp> e) -> bool { if (auto f = e.lock()) { (*f)(args...); return false; } return true; }); } }; usage Signal<int> bloopChanged; // ... Signal<int>::Listener bloopResponse = bloopChanged.add([](int i) { ... }); // or decltype(bloopChanged)::Listener bloopResponse = ... // let bloopResponse go out of scope. // or re-assign it // or reset the shared_ptr to disconnect it bloopResponse.reset(); I have made a gist for this too, with a more in-depth example: https://gist.github.com/johnb003/dbc4a69af8ea8f4771666ce2e383047d
I have had a go at this myself also. My efforts can be found at this gist, which will continue to evolve . . . https://gist.github.com/4172757 I use a different style, more similar to the change notifications in JUCE than BOOST signals. Connection management is done using some lambda syntax that does some capture by copy. It is working well so far.