C++ design help for templated virtual function - c++

I'm trying to implement a number of classes based on a a common class that abstracts a thread-pool using boost.threadpool. I've got something that works (in Xcode on osx 10.7.2) but I'm really not sure its good design or if its even safe (largely because of what I've read on-line about the use of virtual member functions with templates). I'm looking for some style advice on the best way to implement something like this. I'm learning as I go along here so I know a lot of this will be 'bad form'...
I have a base class called 'workqueue' like this:
template <typename T>
class Workqueue{
private:
pool *pThreadPool;
public:
Workqueue (int);
void Start (T);
void Schedule (T);
virtual bool Process(T) {return true;}
};
template <typename T> Workqueue<T>::Workqueue(int thread_count){
pThreadPool = new pool(thread_count);
}
template <typename T> void Workqueue<T>::Start(T data){
pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));
pThreadPool->wait();
}
template <typename T> void Workqueue<T>::Schedule(T data){
pThreadPool->schedule(boost::bind(&Workqueue::Process,this, data));
}
I then define a new service based on this class like this:
struct Service1Data{
string item_data;
};
class MyService : public Workqueue<Service1Data> {
public:
MyService (int);
bool Process (Service1Data);
};
MyService::MyService(int workers) : Workqueue<Service1Data>(workers) {}
bool MyService::Process(Service1Data service_data){
cout << "in process (" << service_data.item_data << ")" << endl;
return true;
}
(I've removed as much of the code to keep it simple so as shown would run forever as it continually submits new work). I use the service like this:
MyService *service1 = new MyService(5);
Service1Data x;
x.item_data = "testing";
service1->Start(x);
// will wait until no more work.
delete service1;
so my specific questions:
firstly (and please be gentle...) is this bad form and is there a much better way to do this? (and why?)
secondly - is this even safe given the virtual/template issues? I read somewhere that it should be safe if the class itself is templated and I think I understand the basic vtable issues - but really not sure of the specifics.
thirdly - the base workqueue class needs to have the member definitions in the 'h' file with the class definition for it to link. Not sure why that would be - I imagine it's a linker issue to do with the virtual/template issues and so makes me nervous.
all help gratefully received..
Chris

I think, you shouldn't mix processing of data with processing of queue.
I mean, you shouldn't have Process method in your Workqueue. Data may process itself, or your queue can get processing function as (template?) parameter.
Then you get rid of all your problems with virtual function. YourService class then should agregate Workqueue and may provide process function.
Also, I doubt if you really need Workqueue. You can just use pThreadPool in YourService.
If you need a common interface for services, you should specify it explicitly & separately. Your inheritance chain looks unclear. inheritance means is. Why YourService is Workqueue. I do not believe! I think YourService can use any sort of queue. But usage is aggregation.
EDIT:
Code will look like this:
template<typename Processor>
class WorkQueue
{
public:
WorkQueue(int count, Processor& processor):_processor(processor),_pool(count) {}
template <typename Data>
void schedule(const Data& data)
{
_pool->schedule(std::bind(&Processor::process,_processor, data));
}
template <typename Data>
void run(const Data& data)
{
schedule(data);
_pool->wait();
}
private:
Processor& _processor;
pool _pool;
};
class Service
{
public:
virtual void run() = 0;
virtual ~Service() {}
};
struct ServiceParams
{
int param;
};
class MyService: public Service
{
friend class WorkQueue<MyService>;
public:
MyService(const ServiceParams& params): _params(params), _queue(1, *this) {}
void run() { return _queue.run(_params); }
private:
ServiceParams _params;
WorkQueue<MyService> _queue;
void process(const ServiceParams& params) {std::cout <<"hello, world\n";}
};
EDIT: I originally considered usage as:
ServiceData data;
Service* service = new MyService(data);
service->run();
delete service;

Little things I can obviously point out:
overuse of new and delete when you could create automatic objects.
For example, if your work-queue and the pool have the same lifetime then:
template <typename T> class Workqueue
{
private:
pool threadPool;
// // etc
};
template< typename T >
Workqueue::Workqueue( int numThreads ) : threadPool( numThreads )
{
}
Your base class needs a virtual destructor, and as it stands Start could call Schedule rather than implement the same line of code (with the boost::bind) twice. Ideally the constructor that takes an int member only will be declared explicit.
You probably need logic to wait for threads to complete.

I think a good design should isolate the queuing and work/task separately. In your design, both are tightly coupled. This design is good if you want to create separate pool for every type of work/task.
Another approach is to create a separate Work class containing the process function. Then your MyService will extend Work. And WorkQueue class will accept Work and by that means any derived class too. This approach is more generic in nature. So same worker queue can accept different type of work/task. Below code illustration will clear more.
Just to add this approach can also be used if you want to have different pool for different type of data. It is more flexible in nature.
template <typename T>
class Work{
T data; // contains the actual data to work on
public:
Work(T data) : data(data) {} // constructor to init data
virtual bool Process(T) {return false;} // returns false to tell process failed
T getData() { return data; } // get the data
};
class MyWork : public Work<Service1Data> {
public:
MyService (Service1Data data) :
Work(data) {}
bool Process (Service1Data); // Implement your work specific process func
};
bool MyWork::Process(Service1Data service_data){
cout << "in process (" << service_data.item_data << ")" << endl;
return true;
}
class Workqueue{
private:
pool *pThreadPool;
public:
Workqueue (int);
void Start (Work);
void Schedule (Work);
};
Workqueue::Workqueue(int thread_count){
pThreadPool = new pool(thread_count);
}
void Workqueue::Start(Work workToDo){
pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));
pThreadPool->wait();
}
void Workqueue::Schedule(Work data){
pThreadPool->schedule(boost::bind(&Work::Process,this, workToDo.getData()));
}
Usage
Service1Data x;
x.item_data = "testing";
MyWork myWork(x);
Workqueue wq = new Workqueue(5);
wq->Start(myWork);
// will wait until no more work.
delete service1;
Now to achieve different pools for different type of work/task, create two Workqueue with different pool size and then give one only one type of work and other another type of work.
NOTE: Above code might contain syntax errors, it just there to convey the design. Treat it as pseudo code.

Related

What is an alternative to using templates on a virtual member function?

I am creating a simple event system where multiple listeners can be notified on a specific topic and when an event is fired, it can pass a generic payload to the event, and the listeners will match the format of the fired event. However, because it's not possible to use templates on a virtual function, how else can I achieve this?
class AEventListener
{
public:
template<class T>
struct PayloadObject {
T obj;
};
explicit AEventListener();
virtual ~AEventListener();
//error here because T is undefined. Each PayloadObject may have a different type
virtual void notify(vector<shared_ptr<PayloadObject<T>>> payload) = 0;
};
The notify method is called when an event topic has a listener subscribed, but I want a generic way of just passing a load of random objects to the listener.
For example
fireEvent("test.topic", Payload { 0, "hello", 123 });
//...
listener.notify(payload);
How would I go about this in C++?
I have managed to get around this, although I don't think this is the best way and could slow down performance.
template<class T>
struct PayloadObject : public APayloadObject {
T obj;
PayloadObject(T obj) {
this->obj = obj;
}
~PayloadObject() override {
};
};
struct APayloadObject {
virtual ~APayloadObject();
};
Firing:
vector<shared_ptr<APayloadObject>> payload;
payload.push_back(shared_ptr<PayloadObject<int>>(new PayloadObject<int>(5))); //payload[0] = int - 5
Events::fire(EventKeys::DISCONNECTION_EVENT, payload);
Notifying:
shared_ptr<PayloadObject<int>> number = dynamic_pointer_cast<PayloadObject<int>>(payload[0]);
int id = number.get()->obj; //payload[0] = int - 5
One simple approach is to come up with a common base or common interface for the Payload objects. So that they are not a template class.
struct Payload {
virtual ~Payload() = default;
virtual std::string foo() const;
virtual std::string bar() const;
};
Another way is to use a variant type for the payload objects:
using Message_t = boost::variant<A, B, C>;
and then make AEventListener take the Message_t type so that it doesn't require the member function to be a template.
class AEventListener
{
public:
virtual ~AEventListener();
virtual void notify(std::vector<Message_t> payload) = 0;
};
In C++17 you could use std::variant for this instead of boost.
Yet another way is to skip using a variant, and just make it so that the Listener must implement three different functions, one for each type:
class AEventListener
{
public:
virtual ~AEventListener();
virtual void notifyA(A payload) = 0;
virtual void notifyB(B payload) = 0;
virtual void notifyC(C payload) = 0;
};
More generally, it is pretty difficult in C++ to make a concept like "Function object that is callable with any particular type of arguments". This is in part because... it is not very useful, there is not much that you can do generically with data of ANY type that you can assume nothing about.
So I would suggest that you think hard about refining your Event Listener concept, and make more concrete what it is that objects of this type are ACTUALLY supposed to be required to do.

Low latency callback in C++

I have an event driven application. I want to keep the event handler (EventHandler class capable of many/all events) a common implementation - while allowing the EventSource be changeable (specifically - at compile time).
To couple the EventHandler with the EventSource, I will have to store an instance of handler within the EventSource. I tried to store handlers of various forms:
pointer to an interface of EventHandler (that has public handler methods defined in concrete EventHandler's
instance of std::function - this provided greatest flexibility
However, in both cases, the latency in calling the target method/lambda was quite high (on my test setup about 250ns) - and to worse, was inconsistent. May be due to virtual table and/or heap allocation and/or type erasure ???
In order to reduce this latency, I want to make use of templates.
The best I could come up with is:
template <typename EventHandler>
class EventSource1
{
EventHandler* mHandler;
public:
typedef EventHandler EventHandlerType;
void AssignHandler (EventHandler* handler)
{
this->mHandler = handler;
}
void EventuallyDoCallback (int arbArg)
{
this->mHandler->CallbackFunction (arbArg);
}
};
template <EventSourceType>
class EventSourceTraits
{
typedef EventSourceType::EventHandlerType EventHandlerType;
static void AssignHandler (EventSourceType& source, EventHandlerType* handler)
{
source.AssignHandler(handler);
}
};
class EventHandler
{
public:
void CallbackFunction (int arg)
{
std::cout << "My callback called\n";
}
};
int main ()
{
EventSource1<EventHandler> source; /// as one can notice, EventSource's need not to know the event handler objects.
EventHandler handler;
EventSourceTraits<EventSource1>::AssignHandler (source, &handler);
}
This method impose a restriction that all my EventSource's to be a template classes.
Question is: Is this best way to achieve consistent and low latency to callback? Can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?
Is this best way to achieve consistent and low latency to callback?
As suggested in the comments to the question, I'd rather try and measure to know if that's really a problem and what's the best alternative for you.
There doesn't exist the best way, it mostly depends on the actual problem.
can this code be improved to avoid the event source classes be completely independent of event handler objects' type ?
Maybe the following can be a good point from which to start to achieve that:
#include <iostream>
class EventSource1
{
using invoke_t = void(*)(void *C, int value);
template<typename T, void(T::*M)(int)>
static void proto(void *C, int value) {
(static_cast<T*>(C)->*M)(value);
}
invoke_t invoke;
void *handler;
public:
template<typename T, void(T::*M)(int) = &T::CallbackFunction>
void AssignHandler (T* ref)
{
invoke = &proto<T, M>;
handler = ref;
}
void EventuallyDoCallback (int arg)
{
invoke(handler, arg);
}
};
class EventHandler
{
public:
void CallbackFunction (int arg)
{
std::cout << "My callback called: " << arg << std::endl;
}
};
int main ()
{
EventSource1 source;
EventHandler handler;
source.AssignHandler(&handler);
source.EventuallyDoCallback(42);
}
See it on wandbox.

Event Callback Daemon

I am working on an event daemon in C++ that I would like to use member function callbacks. Basically an event queue would collect events which the daemon continuously services. There is a base class Event struct with an ID and all events would derive from it. I would like the methods registered for each event to use the derived event type in their signature.
struct Event
{
unsigned int eventId;
};
struct EventA : public Event
{
unsigned int x;
unsigned int y;
};
// and struct EventB, EventC (use your imagination...)
const unsigned int EVENT_A = 1;
const unsigned int EVENT_B = 2;
const unsigned int EVENT_C = 3;
class Foo
{
public:
void handlerMethod_A(const EventA& e);
void handlerMethod_B(const EventB& e);
};
class Bar
{
public:
void handlerMethod_C(const EventC& e);
};
Then the Daemon would allow these classes to subscribe their member functions using their 'this' pointer.
class EventDaemon
{
public:
void serviceEvents();
template <class CallbackClass, class EventType>
void subscribe(
const unsigned int eventId,
CallbackClass* classInstancePtr,
void (CallbackClass::*funcPtr)(EventType));
private:
Queue<Event*> eventQueue_;
};
So outside this class you could do something like:
EventDaemon* ed = new EventDaemon();
Foo* foo = new Foo();
Bar* bar = new Bar();
ed->subscribe(EVENT_A, foo, Foo::handlerMethod_A);
ed->subscribe(EVENT_B, foo, Foo::handlerMethod_B);
ed->subscribe(EVENT_C, bar, Bar::handlerMethod_C);
And the EventDaemon loop would be along the lines of
void EventDaemon::serviceEvents()
{
while (true)
{
if (eventQueue_.empty())
{
// yield to other threads
}
else
{
// pop an event out of the FIFO queue
Event e* = eventQueue_.pop();
// somehow look up the callback info and use it
classInstancePtr->*funcPtr(reinterpret_cast<?*>(e));
}
}
}
So my question is how I can store the 'this' pointers and member function pointers in some sort of array by event ID. That way I could look up the 'classInstancePtr' and 'funcPtr' by using e->eventId and the event type as well for the reinterpret cast.
You are working too hard. Use boost functions:
http://www.boost.org/doc/libs/1_47_0/doc/html/function.html
These work whether you have a object or not. They will increase your compile time.
Note, whenever you come across these types of questions where you know many people must have had the same problem, there is probably a simple option and, if it is not in the standard library, it is probably in boost.
In response to Nick, I'm constantly throwing boost function objects into vectors and whatnot.
I've found that, while boost function objects can hold object references, having them do so can lead to bugs with object lifetimes and it is better to have them hold copies of the class objects (you run into the same bugs however you try to hold a reference to a object instance that you don't necessarily control the lifetime of). The pattern:
class Foo
{
struct Member
{
// member variable definitions
};
shared_ptr<Member> m_; // the only real member variable
public:
// etc. including the all-important copy
// constructor and assignment operator and
// don't forget the member function that gets stuck into
// the boost function as a callback!
};
where all the member variables get held in a shared_ptr allows for good performance and you don't have to worry about lifetimes of objects held by function objects because you can copy them by value. Threaded code (what I always seem to be writing nowadays) needs additional things like at least one boost mutex element in Member or some other way to assure values don't get stomped on.
boost::function [or, if your system supports it, std::function] will take care of holding the this pointer quite well, with the added benefit of not requiring an actual object if it isn't necessary. So instead of void (SomeType::*)(EventA) you have std::function<void(EventA)>, and you call std::bind as appropriate.
subscribe(EVENT_A, std::bind(&foo::handleEventA, &foo, std::placeholders::_1));
A trivial wrapper function can be used to provide the same signature as you originally proposed and hide the nasty placeholders.
You do, of course, still have the issue of each event type having its own signature, and the need to ensure you use the correct Event ID code. In both cases, your base Event type can help out. Your callback need not accept an EventA&; it can accept an Event&, and dynamic_cast it to an EventA at runtime. For the ID, query the type directly.
struct Event {
virtual void ~Event() { }
virtual int ID() =0;
};
template<typename E>
struct EventHelper : Event {
virtual int ID() { return E::EventID; }
};
struct EventA : EventHelper<EventA> {
static const int EventID = 89;
};
Now, if you have an Event* object [when you go to dispatch your events], you can do p->ID() to get the appropriate ID, and if you have a EventA type [when you register your callbacks] you can do EventA::EventID.
So now, all you have to store is a std::function<void(const Event&)> and an associated int value for each of your callbacks, no matter what the actual type of event you have.
void subscribe(int id, std::function<void(const Event&)> f) {
callbacks.insert(std::make_pair(id, f));
}
template<typename E>
void subscribe(std::function<void(const Event&)> f) {
subscribe(E::EventID, f);
}
template<typename O, typename E>
void subscribe(O* p, void (O::*f)(const Event&)) {
subscribe<E>(std::bind(f, p, std::placeholders::_1));
}
You still have the issue that user error when subscribing can result in a function being called incorrectly. If you've used dynamic_cast correctly within the callback, this will get caught at runtime, but a compile time check would be nice. So what if we automate that dynamic_cast? For this step, I'm going to use c++11 lambdas, but it can be implemented in C++03 as well using a variety of methods.
template <class CallbackClass, class EventType>
void subscribe(CallbackClass* classInstancePtr, void (CallbackClass::*funcPtr)(EventType)) {
subscribe<EventType::EventID>([&](const Event& e) {
(classInstancePtr->*funcPtr)(dynamic_cast<const EventType&>(e));
});
}
So now we've gone full circle back to your original interface where your callbacks accept the actual type they are going to be working on, but internally you've squeezed them all into a common signature.
Okay, so I finished an implementation of my original desired interface. I was looking through Dennis' answer but eventually got lead to functors and I realized what I was looking for was a simple polymorphic solution. I failed to grasp before that I could create a non-templated base class with which to use for storing templated classes in vectors/arrays. I think this is what mheyman was trying to tell me... so I apologize I didn't get it right away. Just to clarify though I was really looking for the implementation solution for my own benefit and knowledge, not just a 3rd party library to get the job done. So I guess I would be looking for how Boost functions work, not just that they exist and are awesome.
If anyone is still interested here are the important parts of what I ended up with (minus some extraneous stuff and error checking):
EventFunctor is basically a pointer to member function template class
EventFunctorBase is the non-templated base class used to store them in a vector
The Event is dynamic cast using the templated type before being used to invoke the callback
class EventDaemon
{
public:
template <class CallbackClass, class EventType>
void subscribe(
const EventId eventId,
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&));
private:
EventFunctorBase* callbacks_[MAX_NUM_EVENTS];
};
template <class CallbackClass, class EventType>
void EventDaemon::subscribe(
const EventId eventId,
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&))
{
callbacks_[eventId] = new EventFunctor<CallbackClass,EventType>(callbackClassInstancePtr,funcPtr);
}
class EventFunctorBase
{
public:
EventFunctorBase();
virtual ~EventFunctorBase();
virtual void operator()(const Event& e)=0;
};
template <class CallbackClass, class EventType>
class EventFunctor : public EventFunctorBase
{
public:
EventFunctor(
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&));
virtual void operator()(const Event& e);
private:
CallbackClass* callbackClassInstancePtr_;
void (CallbackClass::*funcPtr_)(const EventType&);
};
template <class CallbackClass, class EventType>
EventFunctor<CallbackClass,EventType>::EventFunctor(
CallbackClass* callbackClassInstancePtr,
void (CallbackClass::*funcPtr)(const EventType&))
:
callbackClassInstancePtr_(callbackClassInstancePtr),
funcPtr_(funcPtr)
{
}
template <class CallbackClass, class EventType>
/*virtual*/ void EventFunctor<CallbackClass,EventType>::operator()(const Event& e)
{
(callbackClassInstancePtr_->*funcPtr_)(dynamic_cast<const EventType&>(e));
}
EventDaemon loop
while (true_)
{
if (eventQueue_->empty())
{
// yield to other threads
}
else
{
Event* e = eventQueue_.pop();
(*(callbacks_[e->ID]))(*e);
}
}
My final steps here will be to try and remove the need to have the developer define an ID for each event... of course this might end up a new post later this week.

Storing and later calling member function of unknown class

I am trying to create a threadpool that can run functions from unknown classes. I do not wish to have to create non-members as a proxy.
I have managed to create a working pool & workerthread class and a task structure, all of these are templates.
// ThreadPool.h
/* Threadpool creates N WorkerThreads (each worker has a ptr to the creating pool),
these block until a task is ready then call ThreadPool::doTask() */
template<class T>
struct Task {
Task() : func(0), inst(0) { }
Task(boost::function<void(T*)> function, T* instance) : func(0), inst(0) {
func = function;
inst = instance;
}
void operator()() {
Task::func(inst);
}
T* inst;
boost::function<void(T*)> func;
};
template<class T>
class ThreadPool {
template<class T> friend class WorkerThread;
public:
void addTask(Task<T> task) {
... // Some stuff
}
bool doTask() {
Task<T> task;
... // Gets a task from std::queue
// Check the task actually exists!
if(task.func && task.inst) {
// Do the task
(task)();
}
}
private:
std::queue<Task<T>> mTasks;
};
As is, this code works, providing I determine the class for ThreadPool and Task. But I want to be able to call members of unknown class types. I had considered a void ptr but I could not find a way to convert this to a valid instance ptr. I have also looked into boost::mem_fun but struggled to really get to grips with it.
I have briefly read about C++0x and from what I understand, it should make solving my problem easier but I would like to solve this before then, if at all possible.
Why use a T* at all, instead of just boost::function<void ()>?
That way you can use free functions as well as member functions, and you can simplify your code.
A task for a member on an instance of class X could be queued like this:
poll.add(boost::bind(&X::member, x_instance, other_arguments));
With no casts and no templates in your code.
Update:
Use boost::function instead of your Task class. You then just need to keep track of the instances and call them as appropriate. For example:
class TaskQueue {
std::deque<boost::function<void ()> > m_tasks;
public:
void add(boost::function<void ()> const& f) { m_tasks.push_back(f); }
bool has_task() const { return !m_tasks.empty(); }
void do_task() {
m_tasks.front()();
m_tasks.pop_front();
}
};
int example_enqueue(TaskQueue* tq) {
boost::shared_ptr<RandomClass> rc(new RandomClass);
tq->add(boost::bind(&RandomClass::method, rc, arg_1, arg_whatever));
}
Note that by combining this method with boost::shared_ptr, you get automatic destruction of your objects when the function goes out of scope, if it's the last reference. That makes life a lot easier.
A void* would work. You just have to do a strong reinterpret_cast. But I would not use this solution. Boost has a bunch of ways of creating functor objects: http://www.boost.org/doc/libs/1_46_1/doc/html/function.html

How pass data to 'generic' observer? As arguments or as a single struct?

I am busy adding a generic observer mechanism to a legacy C++ application (using Visual Studio 2010, but not using .Net, so .Net delegates are out of the question).
In the design I want to separate the application-specific part as much as possible from the generic observer mechanism.
The most logical way of implementing observers seems this way:
class IDoThisObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
For every type of observer (IDoThisObserver, IDoThatObserver, ...) the arguments of the methods (handleDoThis, handleDoThat) are different.
What remains in a generic way of storing the observers, like this:
template<typename T>
class ObserverContainer
{
public:
void addObserver (T &t) {m_observers.push_back(&t);}
private:
std::list<T*> m_observers;
};
Calling an observer can't be generalized since the arguments are different for every observer type.
An alternative way would be to 'pack' all arguments into one argument, like this:
struct DoThisInfo
{
DoThisInfo (int arg1, int arg2) : m_arg1(arg1), m_arg2(arg2) {}
int m_arg1;
int m_arg2;
};
And then define a more generic observer, like this:
template<typename T>
class IObserver
{
public:
void notify(const T &t) = 0;
};
And a collection of these observers would then become this:
template<typename T>
class ObserverContainer
{
public:
void addObserver (IObserver<T> &obs) {m_observers.push_back(&obs);}
private:
std::list<IObserver<T>*> m_observers;
};
Now, much more logic can be centrally added to this ObserverContainer, including calling all observers. The 'initiator' of the call only needs to create and fill in the notification structure.
Classes that want to inherit from multiple kinds of observers, need to do it like this:
class MyObserver : public IObserver<NotifyThis>, public IObserver<NotifyThat>
{
...
};
Which of these approaches (observers with multiple explicit arguments or with one struct argument) seems the best? Are there any advantages or disadvantages to either of these approaches?
EDIT: I looked a bit further to alternative approaches, and the Slot/Signal approach seems another good candidate. Are there any important disadvantages in Slot/Signal that I should know of?
Why not just do:
class IObserver {
// whatever is in common
};
class IDoThisObserver : public IObserver
{
public:
void handlDoThis(int arg1, int arg2) = 0;
};
class IDoThatObserver : public IObserver
{
public:
void handlDoThat(double arg1) = 0;
};
?
Then you have:
class ObserverContainer
{
public:
void addObserver (IObserver* t) {m_observers.push_back(t);}
private:
std::list<IObserver*> m_observers;
};
The design with the struct argument is definitely better as it allows for generic code to be written in the ObserverContainer. It's generally a good design practice to replace longish argument lists with objects that encapsulate the arguments and this is a good example of the payoff. By creating a more general abstraction for your notify method (with the struct you're defining notify as a method that takes a chunk of "data" whereas with the arg list you're defining a method that takes two numbers) you allow yourself to write generic code that uses the method and doesn't have to concern itself with the exact composition of the passed in chunk of data.
Have you looked into Boost.Signals? Better than to reimplement the wheel.
As for Parameters: Calling an observer/slot should conceptionally be the same as if you would call an ordinary function. Most SignalSlots-Implementations allow multiple Parameters, so use it. And please use different signals for different observer types, then there is no need to pass around data in Variants.
Two Disadvantages of the Observer-Pattern/SignalSlots i have seen:
1) Program flow is difficult or even impossible to understand by looking only at the source.
2) Heavily dynamic programs with lots of Observers/SignalSlots may encounter a "delete this"
Everything aside, i like Observers/SignalSlots more than subclassing and thus high coupling.
I don't think either of your approaches would fit your requirement as is. However a little modification using a DataCarrier containing the dataset passed across all the observers wherein each observer would know what to read would do the trick. The sample code below might clear it (note i have not compiled)
enum Type {
NOTIFY_THIS,
NOTIFY_THAT
};
struct Data {
virtual Type getType() = 0;
};
struct NotifyThisData: public Data {
NotifyThisData(int _a, int _b):a(_a), b(_b) { }
int a,b;
Type getType() { return NOTIFY_THIS; }
};
struct NotifyThatData: public Data {
NotifyThatData(std::string _str):str(_str) { }
std::string str;
Type getType() { return NOTIFY_THAT; }
};
struct DataCarrier {
std::vector<Data*> m_TypeData;
};
class IObserver {
public:
virtual void handle(DataCarrier& data) = 0;
};
class NotifyThis: public virtual IObserver {
public:
virtual void handle(DataCarrier& data) {
vector<Data*>::iterator iter = find_if(data.m_TypeData.begin(), data.m_TypeData.end(), bind2nd(functor(), NOTIFY_THIS);
if (iter == data.m_TypeData.end())
return;
NotifyThisData* d = dynamic_cast<NotifyThisData*>(*iter);
std::cout << "NotifyThis a: " << d->a << " b: " << d->b << "\n";
}
};
class NotifyThat: public virtual IObserver {
public:
virtual void handle(DataCarrier& data) {
vector<Data*>::iterator iter = find_if(data.m_TypeData.begin(), data.m_TypeData.end(), bind2nd(functor(),NOTIFY_THAT);
if (iter == data.m_TypeData.end())
return;
NotifyThatData* d = dynamic_cast<NotifyThatData*>(*iter);
std::cout << "NotifyThat str: " << d->str << "\n";
}
};
class ObserverContainer
{
public:
void addObserver (IObserver* obs) {m_observers.push_back(obs);}
void notify(DataCarrier& d) {
for (unsigned i=0; i < m_observers.size(); ++i) {
m_observers[i]->handle(d);
}
}
private:
std::vector<IObserver*> m_observers;
};
class MyObserver: public NotifyThis, public NotifyThat {
public:
virtual void handle(DataCarrier& data) { std::cout << "In MyObserver Handle data\n"; }
};
int main() {
ObserverContainer container;
container.addObserver(new NotifyThis());
container.addObserver(new NotifyThat());
container.addObserver(new MyObserver());
DataCarrier d;
d.m_TypeData.push_back(new NotifyThisData(10, 20));
d.m_TypeData.push_back(new NotifyThatData("test"));
container.notify(d);
return 0;
}
This way u need to modify only the enum if u add a new structure.
Also u can use boost::shared_ptr to handle the mess of pointers.
I wouldn't get the syntax right so I'm just going to list the declarations to illustrate the structures. A generic Observer could be made to expect a parameter that is either subclassed to specific forms of your required parameters or is struct including a horizontal mapping of all primitive parameters that will be required by your Observers. Then the ObserverContainer could function as an AbstractFactory and each subclass of the ObserverContainer could be DoThatObserverFactory and DoThisObserverFactory. The factory would build an observer and assign a configuration to the observer to tell it which parameter to expect.
class AbstractObserverFactory {...};
class DoThatObserverFactory : AbstractObserverFactory {...};
class DoThisObserverFactory : AbstractObserverFactory {...};
class ObserverParam {...};
class DoThatObserverParam : ObserverParam {...};
class DoThisObserverParam : ObserverParam {...};
class Observer;
class DoThisObserver : public Observer
{
public:
void handlDoThis(DoThisObserverParam);
};