Simple semi-compile-time 'event-bus' in c++ - c++

I am trying to hide serious design flaws in my application behind event bus :)
I created template class with static event handlers holder like this:
template<typename ET>
class EventHandler;
template<typename ET>
class EventBus {
public:
static std::vector<EventHandler<ET>*> handlers;
};
template<typename ET>
std::vector<EventHandler<ET>*> EventBus<ET>::handlers = std::vector<EventHandler<ET>*>();
Next part of my 'event-bus' is template class for event handler:
template<typename ET>
class EventHandler {
public:
EventHandler() {
EventBus<ET>::handlers.emplace_back(this);
}
~EventHandler() {
auto &handlers=EventBus<ET>::handlers;
handlers.erase(std::remove(handlers.begin(), handlers.end(), this), handlers.end());
}
virtual void handle_event(ET &event) = 0;
};
Here is a complete code with example usage:
#include <iostream>
#include <vector>
#include <map>
template<typename ET>
class EventHandler;
template<typename ET>
class EventBus {
public:
static std::vector<EventHandler<ET>*> handlers;
};
template<typename ET>
std::vector<EventHandler<ET>*> EventBus<ET>::handlers = std::vector<EventHandler<ET>*>();
template<typename ET>
class EventHandler {
public:
EventHandler() {
EventBus<ET>::handlers.emplace_back(this);
}
~EventHandler() {
auto &handlers=EventBus<ET>::handlers;
handlers.erase(std::remove(handlers.begin(), handlers.end(), this), handlers.end());
}
virtual void handle_event(ET &event) = 0;
};
template<typename ET>
void publish(ET &event) {
for(auto handler : EventBus<ET>::handlers) {
handler->handle_event(event);
}
}
class MyClass: public EventHandler<int>, public EventHandler<std::string> {
public:
void handle_event(int &event) override {
std::cout << "handling int event" << std::endl;
}
void handle_event(std::string &event) override {
std::cout << "handling std::string event" << std::endl;
}
};
int main() {
int eventData = 1;
MyClass eventHandler2;
std::string stringEventData = "hello, world";
{
MyClass eventHandler;
publish(eventData);
publish(stringEventData);
}
publish(eventData);
publish(stringEventData);
std::cout << "Hello, World!" << std::endl;
return 0;
}
So the question is - is there some chance that I will have issues with resolving correct handle_event method based on event type with mulptiple inheritance?

There won't be any issues resolving the handle_event overload with the given publish() function, because only one handle_event member function is actually visible in this function: the type being invoked on is EventHandler<ET>, which only knows about handle_event(ET&) (whatever ET happens to be).
For example, if ET is int, this function doesn't even see the handle_event(std::string&) overload because that overload isn't part of the EventHandler<int> interface.

Related

Pass base class to function delegate that recives the super class

I have this map:
map<IEvent, EventHandler, IEventCompare>
Where EventHandler is defined as typedef void (*EventHandler)(IEvent);
IEvent is a class that describes a general event.
Now I want to add to this map a function that receives CreationEvent, a class that inherits IEvent. The function is defined so:
void onCreate(CreationEvent);
But when I try to add it to the map, I get a compilation error
E0167 argument of type "void (Engine::IObject::*)(Engine::CreationEvent)" is incompatible with parameter of type "Engine::EventHandler"
And if I try to explicitly convert it to EventHandler:
E0171 invalid type conversion
I can declare onCreate with IEvent, but I would like to avoid it since it will require me to assume the type of event, and it is not well defined.
Is there a way to do what I try?
IEvent:
/**
* Represents an Event, such as collision between 2 objects or click on an object.
*/
class IEvent
{
public:
IEvent(string name) { this->name = name; };
/**
* Copy constructor.
*/
IEvent(const IEvent& other) { this->name = other.name;};
string getName() const { return this->name; };
protected:
string name;
};
CreationEvent:
class CreationEvent : public IEvent
{
public:
CreationEvent();
std::chrono::time_point<std::chrono::system_clock> getCreateTime() const;
private:
std::chrono::time_point<std::chrono::system_clock> creationTime; /**< The creation time of this event.*/
};
Notes:
Everything is inside namespace Engine, and the map is declared inside IObject.
If I get your idea right, you want:
Have typed events with base event class.
Have handlers with base handler class.
Handlers can receive event of certain type.
Consider the next example. For the simplicity I used std::vector instead of std::map, and put it inside event class.
This code contains ugliness, leaks and must not be used in a "production" without modifications.
#include <iostream>
#include <vector>
//***********************************************************//
struct event;
struct handler
{
};
struct event_handler
{
event_handler(handler* receiver) : receiver_{ receiver } {}
handler* receiver_;
virtual void invoke(event& evt) = 0;
};
template <typename T, typename U>
struct event_handler_impl : event_handler
{
typedef void (T::* handler_function)(U&);
event_handler_impl(handler* receiver, handler_function function) :
event_handler{ receiver_ },
function_{ function } {}
void invoke(event& evt) {
T* typed_receiver = static_cast<T*>(receiver_);
U& typed_event = static_cast<U&>(evt);
(typed_receiver->*function_)(typed_event);
}
handler_function function_;
};
struct event
{
void subscribe(event_handler* hdlr)
{
//TODO: Check. Is double added?
handlers_.push_back(hdlr);
}
void sent()
{
for (auto& item : handlers_)
{
item->invoke(*this);
}
}
std::vector<event_handler*> handlers_;
};
//*****************************EXAMPLE***********************//
struct creation_event : public event
{
int creation_id{};
};
struct bar_handler : public handler
{
void handle_creation(creation_event& evt)
{
std::cout << "bar" << evt.creation_id << std::endl;
}
};
struct foo_handler : public handler
{
void handle_creation(creation_event& evt)
{
std::cout << "foo" << evt.creation_id << std::endl;
}
};
template<typename T, typename U>
void subscribe_to_event(U& evt, T* reciver, void (T::* handler_function)(U&))
{
evt.subscribe(new event_handler_impl<T, U>(reciver, handler_function));
}
int main()
{
creation_event evt;
bar_handler bar;
foo_handler foo;
subscribe_to_event(evt, &foo, &foo_handler::handle_creation);
subscribe_to_event(evt, &bar, &bar_handler::handle_creation);
evt.sent();
evt.creation_id = 1;
evt.sent();
return 0;
}
The only tricky part is:
template <typename T, typename U>
struct event_handler_impl : event_handler
Here we generating classes for storing our typed “callback” and using polymorphism to store those classes inside our std::vector since they are all child classes for handler.
As a suggestion - consider using smart pointers instead of raw pointers. Also you can put function void subscribe_to_even(…) to the handler base class, so you can remove second parameter and just pass "this" to the event_handler_impl - new event_handler_impl<T, U>(this, handler_function)

My concept for template class hiding breaks data member inheritance. Why?

I would like to add support for protobuf events to my existing event queue.
I wrote a proof-of-concept (see below), which seemed to work fine at first. Unfortunately, compilation aborts when accessing inherited data members. Accessing inherited function members works just fine. Following classes are declared:
Abstract class SubscriptionHolderBase: This is the interface base class
Abstract class SubscriptionHolderCommon: Is supposed to hold the common members of the following two classes.
Depending on the type of the event to subscribe, the compiler shall automatically choose one of the following classes to instantiate:
Template class SubscriptionHolder, specialized for normal events.
Templace class SubscriptionHolder, spezialized for all other (i.e. protobuf) events.
For some reason, SubscriptionHolder seems to inherit the function member of SubscriptionHolderCommon just fine, but when trying to access the data member _member_var (inherited from the same class), following error message is produced:
error: ‘_member_var’ was not declared in this scope
Can someone explain the reason?
My proof-of-concept code:
When commenting all 3 occurences of _member_var, it is doing just fine.
You can compile and let it run online here.
#include <type_traits>
#include <iostream>
class ProtobufEvent {
};
class Proto2 : public ProtobufEvent {
};
class Event {
public:
virtual ~Event(){};
};
class SubscriptionHolderBase {
public:
virtual bool dispatch(Event* event) = 0;
virtual void handle() = 0;
};
template<typename EventType>
class SubscriptionHolderCommon : public SubscriptionHolderBase {
public:
void handle() {
std::cout << "Member function of unspecialized common base class called." << std::endl;
}
protected:
bool _member_var;
};
template<typename EventType, typename Enable = void>
class SubscriptionHolder;
template<typename EventType>
class SubscriptionHolder<EventType, typename std::enable_if<std::is_convertible<EventType*, ProtobufEvent*>::value>::type> : public SubscriptionHolderCommon<EventType> {
public:
virtual bool dispatch(Event* event);
};
template<typename EventType>
class SubscriptionHolder<EventType, typename std::enable_if<!std::is_convertible<EventType*, ProtobufEvent*>::value>::type> : public SubscriptionHolderCommon<EventType> {
public:
virtual bool dispatch(Event* event);
};
template<typename EventType>
bool SubscriptionHolder<EventType, typename std::enable_if<std::is_convertible<EventType*, ProtobufEvent*>::value>::type>::dispatch(Event* event) {
std::cout << "Member function of specialized class for protobuf event called." << std::endl;
_member_var = true;
return false;
}
template<typename EventType>
bool SubscriptionHolder<EventType, typename std::enable_if<!std::is_convertible<EventType*, ProtobufEvent*>::value>::type>::dispatch(Event* event) {
std::cout << "Member function of specialized class for standard event called." << std::endl;
_member_var = true;
return false;
}
int main() {
Event* e = nullptr;
SubscriptionHolderBase* ptr;
SubscriptionHolder<Event> s_e;
ptr = &s_e;
ptr->handle();
ptr->dispatch(e);
std::cout << std::endl;
SubscriptionHolder<ProtobufEvent> s_p;
ptr = &s_p;
ptr->handle();
ptr->dispatch(e);
std::cout << std::endl;
SubscriptionHolder<Proto2> s_p2;
ptr = &s_p2;
ptr->handle();
ptr->dispatch(e);
std::cout << std::endl;
}

How to store an std::vector if the type needs a template

I'm trying to store a std::vector inside a class called InputManager that stores listeners.
I have a superclass called Listener. This class has variables with types stored in a template.
template <class obj>
class Listener
The derived class defines the template based on the listener type:
class onClickListener : public Listener<Button*>
How can I store a std::vector of Listeners without defining the type?
std::vector<Listener<obj>> InputManager::listeners = std::vector<Listener<obj>>()
class InputManager
{
public:
template <class obj>
InputManager(GLFWwindow* window);
template <class obj>
void run();
~InputManager();
static InputManager* Self;
template<class obj>
void addListener(Listener<obj>* listener);
private:
GLFWwindow* window;
static std::vector<Listener<obj>> InputManager::listeners = std::vector<Listener<obj>>();
};
If you need more information please let me know.
Without a template you just have a plain non-template base type:
#include <vector>
#include <iostream>
#include <utility>
#include <memory>
struct listener { virtual void action() = 0; virtual ~listener() {} };
struct onClickListener : listener {
void action() override { std::cout << "click: " << this << '\n'; }
};
struct input_manager
{
std::vector<listener*> listeners;
void add_listener(listener* lisp)
{
listeners.push_back(lisp);
}
void doit() const
{
for(auto&& lp : listeners) { if(lp) lp->action(); }
}
};
int main()
{
input_manager mgr;
auto listen_a = std::make_unique<onClickListener>();
auto listen_b = std::make_unique<onClickListener>();
mgr.add_listener(listen_a.get());
mgr.add_listener(listen_b.get());
mgr.doit();
}

Using the Visitor Pattern with template derived classes

I try to implement the Visitor pattern with templated derived classes
I work with gcc 4.5
here is the VisitorTemplate.hpp, I specialized Derived in the class Visitor, but I'd like to be able to handle any type:
edit : thanks to the suggestions of interjay, the code compiles and runs without errors now
#ifndef VISITORTEMPLATE_HPP_
#define VISITORTEMPLATE_HPP_
#include <iostream>
#include <string>
using namespace std;
template<class T> Derived;
class Visitor
{
public:
virtual void visit(Derived<string> *e) = 0;
};
class Base
{
public:
virtual void accept(class Visitor *v) = 0;
};
template<class T>
Derived: public Base
{
public:
virtual void accept(Visitor *v)
{
v->visit(this);
}
string display(T arg)
{
string s = "This is : " + to_string(arg);
return s;
}
};
class UpVisitor: public Visitor
{
virtual void visit(Derived<string> *e)
{
cout << "do Up on " + e->display("test") << '\n';
}
};
class DownVisitor: public Visitor
{
virtual void visit(Derived<string> *e)
{
cout << "do Down on " + e->display("test") << '\n';
}
};
#endif /* VISITORTEMPLATE_HPP_ */
main.cpp
Base* base = new Derived<string>();
Visitor* up = new UpVisitor();
Visitor* down = new DownVisitor();
base->accept(up);
base->accept(down);
Now my goal is to use Derived in visit without specializing; unfortunately, visit is a virtual method so I can't template it
From Modern C++ - Design Generic Programming and Design Patterns Applied - Andrei Alexandrescu
#include <iostream>
class BaseVisitor
{
public:
virtual ~BaseVisitor() {};
};
template <class T, typename R = int>
class Visitor
{
public:
virtual R visit(T &) = 0;
};
template <typename R = int>
class BaseVisitable
{
public:
typedef R ReturnType;
virtual ~BaseVisitable() {};
virtual ReturnType accept(BaseVisitor & )
{
return ReturnType(0);
}
protected:
template <class T>
static ReturnType acceptVisitor(T &visited, BaseVisitor &visitor)
{
if (Visitor<T> *p = dynamic_cast< Visitor<T> *> (&visitor))
{
return p->visit(visited);
}
return ReturnType(-1);
}
#define VISITABLE() \
virtual ReturnType accept(BaseVisitor &v) \
{ return acceptVisitor(*this, v); }
};
/** example of use */
class Visitable1 : public BaseVisitable<int>
{
/* Visitable accept one BaseVisitor */
public:
VISITABLE();
};
class Visitable2 : public BaseVisitable<int>
{
/* Visitable accept one BaseVisitor */
public:
VISITABLE();
};
class VisitorDerived : public BaseVisitor,
public Visitor<Visitable1, int>,
public Visitor<Visitable2, int>
{
public:
int visit(Visitable1 & c)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int visit(Visitable2 & c)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main(int argc, char **argv)
{
VisitorDerived visitor;
Visitable1 visitable1;
Visitable2 visitable2;
visitable1.accept(visitor);
visitable2.accept(visitor);
}
Is possible to avoid dynamic_cast with CRTP pattern like:
#include <iostream>
class BaseVisitor
{
public:
virtual ~BaseVisitor() {};
};
template <class T>
class Visitor
{
public:
virtual void visit(T &) = 0;
};
template <class Visitable>
class BaseVisitable
{
public:
template <typename T>
void accept(T & visitor)
{
visitor.visit(static_cast<Visitable &>(*this));
}
};
/** example of use */
class Visitable1 : public BaseVisitable<Visitable1>
{
};
class Visitable2 : public BaseVisitable<Visitable2>
{
};
class VisitorDerived : public BaseVisitor,
public Visitor<Visitable1>,
public Visitor<Visitable2>
{
public:
void visit(Visitable1 & c)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void visit(Visitable2 & c)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main(int argc, char **argv)
{
VisitorDerived visitor;
Visitable1 visitable1;
Visitable2 visitable2;
visitable1.accept<VisitorDerived>(visitor);
visitable2.accept<VisitorDerived>(visitor);
}
Your Derived class cannot use Visitor because it hasn't been defined yet (it was only forward declared, and is therefore an incomplete type).
You can fix the compile error by putting the Visitor definition before Derived. You will also need to forward-declare Derived before defining Visitor:
template <class T> class Derived;
class Visitor {
public:
virtual void visit(Derived<string> *e) = 0;
};
template <class T>
class Derived : public Base {
//.... can call Visitor methods here ...
};

How to convert an existing callback interface to use boost signals & slots

I've currently got a class that can notify a number of other objects via callbacks:
class Callback {
virtual NodulesChanged() =0;
virtual TurkiesTwisted() =0;
};
class Notifier
{
std::vector<Callback*> m_Callbacks;
void AddCallback(Callback* cb) {m_Callbacks.push(cb); }
...
void ChangeNodules() {
for (iterator it=m_Callbacks.begin(); it!=m_Callbacks.end(); it++) {
(*it)->NodulesChanged();
}
}
};
I'm considering changing this to use boost's signals and slots as it would be beneficial to reduce the likelihood of dangling pointers when the callee gets deleted, among other things. However, as it stands boost's signals seems more oriented towards dealing with function objects. What would be the best way of adapting my code to still use the callback interface but use signals and slots to deal with the connection and notification aspects?
Compared to my other answer, this solution is much more generic and eliminates boilerplate code:
#include <iostream>
#include <boost/bind.hpp>
#include <boost/signal.hpp>
///////////////////////////////////////////////////////////////////////////////
// GENERIC REUSABLE PART FOR ALL SUBJECTS
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
template <class CallbackType>
class CallbackInvoker
{
public:
virtual ~CallbackInvoker() {}
virtual void operator()(CallbackType* callback) const {};
};
//-----------------------------------------------------------------------------
template <class CallbackType, class Binding>
class BoundInvoker : public CallbackInvoker<CallbackType>
{
public:
BoundInvoker(const Binding& binding) : binding_(binding) {}
void operator()(CallbackType* callback) const {binding_(callback);}
private:
Binding binding_;
};
//-----------------------------------------------------------------------------
template <class CallbackType>
class CallbackSlot
{
public:
CallbackSlot(CallbackType* callback) : callback_(callback) {}
void operator()(const CallbackInvoker<CallbackType>& invoker)
{invoker(callback_);}
private:
CallbackType* callback_;
};
//-----------------------------------------------------------------------------
template <class CallbackType>
class Subject
{
public:
virtual ~Subject() {}
boost::signals::connection Connect(CallbackType* callback)
{return signal_.connect(CallbackSlot<CallbackType>(callback));}
protected:
template <class Binding> void Signal(const Binding& binding)
{
signal_(BoundInvoker<CallbackType,Binding>(binding));
}
private:
boost::signal<void (const CallbackInvoker<CallbackType>&)> signal_;
};
///////////////////////////////////////////////////////////////////////////////
// THIS PART SPECIFIC TO ONE SUBJECT
///////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------------------------------
class MyCallback
{
public:
virtual ~MyCallback() {}
virtual void NodulesChanged() =0;
virtual void TurkiesTwisted(int arg) =0;
};
//-----------------------------------------------------------------------------
class FooCallback : public MyCallback
{
public:
virtual ~FooCallback() {}
void NodulesChanged() {std::cout << "Foo nodules changed\n";}
void TurkiesTwisted(int arg)
{std::cout << "Foo " << arg << " turkies twisted\n";}
};
//-----------------------------------------------------------------------------
class BarCallback : public MyCallback
{
public:
virtual ~BarCallback() {}
void NodulesChanged() {std::cout << "Bar nodules changed\n";}
void TurkiesTwisted(int arg)
{std::cout << "Bar " << arg << " turkies twisted\n";}
};
//-----------------------------------------------------------------------------
class MySubject : public Subject<MyCallback>
{
public:
void OnNoduleChanged()
{this->Signal(boost::bind(&MyCallback::NodulesChanged, _1));}
void OnTurkiedTwisted(int arg)
{this->Signal(boost::bind(&MyCallback::TurkiesTwisted, _1, arg));}
};
///////////////////////////////////////////////////////////////////////////////
// CLIENT CODE
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
int main()
{
MySubject subject;
FooCallback fooCb;
BarCallback barCb;
subject.Connect(&fooCb);
subject.Connect(&barCb);
subject.OnNoduleChanged();
subject.OnTurkiedTwisted(42);
}
Hooray for boost::bind! :-)
boost::signals is pretty flexible when it comes to what you bind to a signal. You can use a function object, but you can also just use a function pointer or use boost::bind to make almost any kind of function into a function object. Here is what your example might look like, although there may be better ways.
#include <boost/signals.hpp>
class Notifier
{
public:
boost::signal< void() > NodulesChanged;
void ChangeNodules()
{
//Just call the signal and all connected slots will be called.
NodulesChanged();
}
};
To add a callback, you can simply
void callback1()
{
//do callback stuff
}
void callback2()
{
//do callback stuff
}
int main()
{
Notifier n;
n.NodulesChanged.connect(&callback1);
n.NodulesChanged.connect(&callback2);
//calls callback1 & 2.
n.ChangeNodules();
}
If you wanted to connect a member function with arguments as a slot, you could do something like this:
class Notifier
{
public:
boost::signal< void ( double ) > ProgressSignal;
};
class OtherClass
{
public:
void UpdateProgress(double pct);
};
int main()
{
Notifier n;
OtherClass oc;
n.ProgressSignal.connect(boost::bind(&OtherClass::UpdateProgress, &oc, _1));
//Calls oc.UpdateProgress(0);
n.ProgressSignal(0);
}
Warning: None of this has been compiled or tested.
This solution allows you to use the same signal object even if Callback's methods have different signatures.
#include <iostream>
#include <boost/signal.hpp>
//------------------------------------------------------------------------------
class Callback
{
public:
virtual void NodulesChanged() =0;
virtual void TurkiesTwisted(int arg) =0;
};
//------------------------------------------------------------------------------
class FooCallback : public Callback
{
public:
void NodulesChanged() {std::cout << "Foo nodules changed\n";}
void TurkiesTwisted(int arg) {std::cout << "Foo " << arg << " turkies twisted\n";}
};
//------------------------------------------------------------------------------
class BarCallback : public Callback
{
public:
void NodulesChanged() {std::cout << "Bar nodules changed\n";}
void TurkiesTwisted(int arg) {std::cout << "Bar " << arg << " turkies twisted\n";}
};
//------------------------------------------------------------------------------
class CallbackInvoker
{
public:
virtual void operator()(Callback* callback) const {};
};
//------------------------------------------------------------------------------
class NoduleChangedInvoker : public CallbackInvoker
{
public:
void operator()(Callback* callback) const {callback->NodulesChanged();}
};
//------------------------------------------------------------------------------
class TurkiesTwistedInvoker : public CallbackInvoker
{
public:
TurkiesTwistedInvoker(int arg) : arg_(arg) {}
void operator()(Callback* callback) const {callback->TurkiesTwisted(arg_);}
private:
int arg_;
};
//------------------------------------------------------------------------------
class CallbackSlot
{
public:
CallbackSlot(Callback* callback) : callback_(callback) {}
void operator()(const CallbackInvoker& invoker) {invoker(callback_);}
private:
Callback* callback_;
};
//------------------------------------------------------------------------------
class Subject
{
public:
typedef boost::signal<void (const CallbackInvoker&)> SignalType;
boost::signals::connection Connect(Callback* callback)
{return signal_.connect(CallbackSlot(callback));}
void OnNoduleChanged() {signal_(NoduleChangedInvoker());}
void OnTurkiedTwisted(int arg) {signal_(TurkiesTwistedInvoker(arg));}
private:
SignalType signal_;
};
//------------------------------------------------------------------------------
int main()
{
Subject subject;
FooCallback fooCb;
BarCallback barCb;
subject.Connect(&fooCb);
subject.Connect(&barCb);
subject.OnNoduleChanged();
subject.OnTurkiedTwisted(42);
}
This outputs:
Foo nodules changed
Bar nodules changed
Foo 42 turkies twisted
Bar 42 turkies twisted
CallbackSlot is the function object stored in the boost::signal, and contains a pointer to a concrete Callback object. When you invoke the boost::signal, you have to pass it a CallbackInvoker concrete object which bundles any callback arguments and which knows how to invoke the appropriate Callback method.
There might be a way to avoid the CallbackInvoker boilerplate code using Boost.Lamda, but I'm not very familiar with that Boost library.
You'll probably want to use boost::shared_ptr<Callback> instead of Callback* to avoid memory leaks and dangling pointers.