Forked from psfrolov/boost_signals2_plus_fusion.cc
Last active
August 29, 2015 14:19
-
-
Save antmd/971114a3270024fe2c55 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <utility> | |
#include <boost/fusion/container/map.hpp> | |
#include <boost/fusion/sequence/intrinsic/at_key.hpp> | |
#include <boost/fusion/sequence/intrinsic/value_at_key.hpp> | |
#include <boost/optional.hpp> | |
#include <boost/signals2.hpp> | |
#include <boost/utility/in_place_factory.hpp> | |
namespace util { | |
//----------------------------------------------------------------------------- | |
// EventSource - "Observable" mixin. | |
//----------------------------------------------------------------------------- | |
// Encapsulates event handler function signature. | |
template<typename Signature> struct EventHandler { | |
public: | |
EventHandler(const EventHandler&) = delete; | |
EventHandler& operator=(const EventHandler&) = delete; | |
EventHandler() = default; | |
private: | |
template<typename Events> friend class EventSource; | |
using Signal = boost::optional< | |
typename boost::signals2::signal_type< | |
Signature, | |
boost::signals2::keywords::mutex_type<boost::signals2::dummy_mutex> | |
>::type | |
>; | |
using SignalResult = typename Signal::value_type::result_type; | |
Signal signal_; | |
}; | |
// Generic event source mixin - users must derive from it. | |
template<typename Events> class EventSource { | |
private: | |
using EventTable = typename Events::EventTable; | |
// Helper type generator. | |
template<typename Event> struct GetType { | |
// EventHandler type. | |
using EventHandler = typename boost::fusion::result_of:: | |
value_at_key<EventTable, Event>::type; | |
// boost::optional type. | |
using Signal = typename EventHandler::Signal; | |
// Slot return type. | |
using SignalResult = typename EventHandler::SignalResult; | |
}; | |
public: | |
// Connects event handler. | |
template<typename Event, typename Handler> | |
boost::signals2::connection Connect(Handler&& handler) { | |
return GetSignal<Event>()->connect(std::forward<Handler>(handler)); | |
} | |
protected: | |
EventSource() = default; | |
// Generates n-ary signal. | |
template<typename Event, typename... Args> | |
typename GetType<Event>::SignalResult FireEvent(Args&&... args) const { | |
return GetSignal<Event>()->operator()(std::forward<Args>(args)...); | |
} | |
private: | |
// Creates signal if not exists, returns reference to it. | |
template<typename Event> | |
typename GetType<Event>::Signal& GetSignal() const { | |
auto& signal = boost::fusion::at_key<Event>(signals_).signal_; | |
if (!signal) | |
signal = boost::in_place(); | |
return signal; | |
} | |
mutable EventTable signals_; | |
}; | |
} // namespace util | |
//----------------------------------------------------------------------------- | |
// Example usage. | |
//----------------------------------------------------------------------------- | |
struct MyEvents { | |
struct EventA; struct EventB; | |
using EventTable = boost::fusion::map< | |
boost::fusion::pair<EventA, util::EventHandler<void()>>, | |
boost::fusion::pair<EventB, util::EventHandler<int(int)>> | |
>; | |
}; | |
class MySource: public util::EventSource<MyEvents> { | |
public: | |
void EmitEventA() { | |
std::cout << "EventA signal generated." << std::endl; | |
FireEvent<MyEvents::EventA>(); | |
} | |
int EmitEventB() { | |
int foo = 3; | |
std::cout << "EventB signal generated. foo = " << foo << std::endl; | |
int bar = *FireEvent<MyEvents::EventB>(foo); | |
std::cout << "EventB signal processed. bar = " << bar << std::endl; | |
return bar; | |
} | |
}; | |
class MyListener { | |
public: | |
void OnEventA() { | |
std::cout << "OnEventA handler called." << std::endl; | |
} | |
int OnEventB(int foo) { | |
std::cout << "OnEventB handler called. foo = " << foo << std::endl; | |
return foo * 2; | |
} | |
}; | |
int main() { | |
MySource event_source; | |
MyListener event_listener; | |
// Connect event handlers. | |
event_source.Connect<MyEvents::EventA>([&]() { | |
event_listener.OnEventA(); | |
}); | |
event_source.Connect<MyEvents::EventB>([&](int foo) { | |
return event_listener.OnEventB(foo); | |
}); | |
// Fire events. | |
event_source.EmitEventA(); | |
event_source.EmitEventB(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//----------------------------------------------------------------------------- | |
// Observable mixin. | |
//----------------------------------------------------------------------------- | |
#include <tuple> | |
#include <utility> | |
#include <boost/signals2.hpp> | |
// Convinience wrapper for boost::signals2::signal. | |
template<typename Signature> class Observer { | |
public: | |
Observer(const Observer&) = delete; | |
Observer& operator=(const Observer&) = delete; | |
Observer() = default; | |
private: | |
template<typename Observers> friend class Observable; | |
using Signal = boost::signals2::signal<Signature>; | |
using SignalResult = typename Signal::result_type; | |
Signal signal_; | |
}; | |
// Generic observable mixin - users must derive from it. | |
template<typename Observers> class Observable { | |
private: | |
using ObserverTable = typename Observers::ObserverTable; | |
public: | |
// Registers an observer. | |
template<size_t ObserverId, typename F> | |
boost::signals2::connection | |
Register(F&& f) { | |
return std::get<ObserverId>(signals_).signal_.connect(std::forward<F>(f)); | |
} | |
protected: | |
Observable() = default; | |
// Notifies observers. | |
template<size_t ObserverId, typename... Args> | |
typename std::tuple_element<ObserverId, ObserverTable>::type::SignalResult | |
Notify(Args&&... args) const { | |
return std::get<ObserverId>(signals_).signal_(std::forward<Args>(args)...); | |
} | |
private: | |
ObserverTable signals_; | |
}; | |
//----------------------------------------------------------------------------- | |
// Example usage. | |
//----------------------------------------------------------------------------- | |
#include <iostream> | |
// Defines observers for Windows class. | |
struct WindowObservers { | |
enum { ShowEvent, CloseEvent }; | |
using ObserverTable = std::tuple< | |
Observer<void()>, // ShowEvent | |
Observer<bool(bool force_close)> // CloseEvent | |
>; | |
}; | |
// Window: our Observable. | |
class Window: public Observable<WindowObservers> { | |
public: | |
void Show() { | |
std::cout << "Window::Show called." << std::endl; | |
Notify<WindowObservers::ShowEvent>(); | |
std::cout << "Window::Show handled." << std::endl << std::endl; | |
} | |
bool Close(bool force_close = false) { | |
std::cout << "Window::Close called: force_close == " | |
<< std::boolalpha << force_close << "." << std::endl; | |
const boost::optional<bool> can_close{ | |
Notify<WindowObservers::CloseEvent>(force_close) }; | |
std::cout << "Window::Close handled. can_close == " | |
<< std::boolalpha << (!can_close || *can_close) << "." | |
<< std::endl << std::endl; | |
const bool closing{ force_close || !can_close || *can_close }; | |
if (closing) { | |
// Actually close the window. | |
// ... | |
} | |
return closing; | |
} | |
}; | |
// Application: our Observer. | |
class Application { | |
public: | |
explicit Application(Window& window) : window_(window) { | |
// Register window observers. | |
window_.Register<WindowObservers::ShowEvent>([this]() { | |
OnWindowShow(); | |
}); | |
window.Register<WindowObservers::CloseEvent>([this](bool force_close) { | |
return OnWindowClose(force_close); | |
}); | |
} | |
private: | |
void OnWindowShow() { | |
std::cout << "Application::OnWindowShow called." << std::endl; | |
} | |
bool OnWindowClose(bool force_close) { | |
std::cout << "Application::WindowClose called: force_close == " | |
<< std::boolalpha << force_close << "." << std::endl; | |
return force_close; | |
} | |
Window& window_; | |
}; | |
int main() { | |
Window window; | |
Application application{ window }; | |
// Notify observers. | |
window.Show(); | |
//... | |
window.Close(false); | |
window.Close(true); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <functional> | |
#include <tuple> | |
#include <utility> | |
namespace util { | |
//----------------------------------------------------------------------------- | |
// EventSource - "Observable" mixin. | |
//----------------------------------------------------------------------------- | |
// Generic event source mixin - users must derive from it. | |
template<typename Events> class EventSource { | |
private: | |
using EventTable = typename Events::EventTable; | |
public: | |
// Connects event handler. | |
template<size_t Event, typename Handler> | |
void Connect(Handler&& handler) { | |
std::get<Event>(signals_) = std::forward<Handler>(handler); | |
} | |
protected: | |
EventSource() = default; | |
// Generates n-ary signal. | |
template<size_t Event, typename... Args> | |
typename std::tuple_element<Event, EventTable>::type::result_type | |
FireEvent(Args&&... args) const { | |
return std::get<Event>(signals_)(std::forward<Args>(args)...); | |
} | |
private: | |
mutable EventTable signals_; | |
}; | |
} // namespace util | |
//----------------------------------------------------------------------------- | |
// Example usage. | |
//----------------------------------------------------------------------------- | |
struct MyEvents { | |
enum Event { EventA, EventB }; | |
using EventTable = std::tuple< | |
std::function<void()>, | |
std::function<int(int)> | |
>; | |
}; | |
class MySource: public util::EventSource<MyEvents> { | |
public: | |
void EmitEventA() { | |
std::cout << "EventA signal generated." << std::endl; | |
FireEvent<MyEvents::EventA>(); | |
} | |
int EmitEventB() { | |
int foo = 3; | |
std::cout << "EventB signal generated. foo = " << foo << std::endl; | |
int bar = FireEvent<MyEvents::EventB>(foo); | |
std::cout << "EventB signal processed. bar = " << bar << std::endl; | |
return bar; | |
} | |
}; | |
class MyListener { | |
public: | |
void OnEventA() { | |
std::cout << "OnEventA handler called." << std::endl; | |
} | |
int OnEventB(int foo) { | |
std::cout << "OnEventB handler called. foo = " << foo << std::endl; | |
return foo * 2; | |
} | |
}; | |
int main() { | |
MySource event_source; | |
MyListener event_listener; | |
// Connect event handlers. | |
event_source.Connect<MyEvents::EventA>([&]() { | |
event_listener.OnEventA(); | |
}); | |
event_source.Connect<MyEvents::EventB>([&](int foo) { | |
return event_listener.OnEventB(foo); | |
}); | |
// Fire events. | |
event_source.EmitEventA(); | |
event_source.EmitEventB(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment