Skip to content

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

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
You can’t perform that action at this time.