Instantly share code, notes, and snippets.

Embed
What would you like to do?
Observable mixins using Boost, the standard library, or a mixture of both.
#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();
}
//-----------------------------------------------------------------------------
// Observable mixin.
//-----------------------------------------------------------------------------
#include <tuple>
#include <utility>
#include <boost/signals2.hpp>
// Convenience 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);
}
#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();
}
@pointer

This comment has been minimized.

Copy link

pointer commented Nov 8, 2016

Hi arkfps,
Thanks for sharing....
Is there any way to make boost_signals2_plus_fusion.cc thread safe?
thanks in advance....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment