Archive

Archive for March, 2011

On creating an asynchronous message-based c++ framework; part 2

March 10, 2011 2 comments

This is the second post in a series on the creation of an asynchronous message-based C++ application framework.

In part 1 I discussed my approach to defining the type for an object that has a set of observers, and finished up with the following types and public interface:

typedef boost::function observer;
struct message;

struct subject
{
   virtual ~subject() {}

   void registerObserver(observer& o);
   void notifyObservers(const message& m);
};

In this part I will look at how to use a message type hierarchy, and why I think doing so is a Good Thing.

So what is it?

Which is more informative: a message that says “Hi there, FYI the value of foo has changed” or one that says “Hey you! foo is now 729”? Most messages make more sense with some sort of attached context, and since now seems a good time to create a message base class, here’s a basic one for us:

struct message
{
   virtual ~message() {}

   typedef std::string message_id;
   virtual message_id id() const = 0;
};

It’s worth noting at this stage that context should be as specific and fine-grained as possible. Whilst attaching a subject to its messages can make implementation faster, it ain’t proper OO design, and it tightly couples observers to their subjects. (If you must do it, you could keep them decoupled by attaching the subject behind a separate interface type, but shhhhh, I didn’t tell you that.)

Topic-based filtering

How much of a pain would it be if observer implementations resembled the following?

void notify(const message& m)
{
   if      (m.id() == MSGID_FOO_CHANGED)  fooChanged(m)
   else if (m.id() == MSGID_USER_SNEEZED) userSneezed(m)
   //...
}

Enough of a pain that your fingers would drop off out of boredom, that’s how much. To avoid the inconvenience of being fingerless, we’ll implement ‘topic-based filtering’ in the subject, meaning that observers will be registered only to receive the message type(s) they’re interested in. This is a common feature of the ‘publish/subscribe’ pattern, of which the observer pattern is a subset.

Here’s the updated subject definition for topic-based filtering:

struct subject
{
   typedef message::message_id message_id;

   void registerObserver(const message_id& id, const observer& o)
   {
      observers[id].push_back(o);
   }

   // NB: I will tackle removeObserver() in a later post

   void notifyObservers(const message& m)
   {
      BOOST_FOREACH (const observer& o, observers[m.id()])
         o(m);
   }

private:
   typedef std::vector<observer> observer_set;
   typedef std::map<message_id, observer_set> observer_map;
   observer_map observers;
};

And a quick example:

void notify(const message& m)
{ /*...*/ }

void foo()
{
   subject s;
   s.registerObserver(foo_updated::MESSAGE_ID, notify);
}

The eagle-eyed among you may have noticed that in this post I’ve defined observer_set to be a vector of observers, whereas in the previous post I defined it as a vector of pointers to observers. That was a typo – albeit consistent throughout – which in a program of any larger size would likely have led down the path of Undefined Behaviour, so apologies for that.

Downcasting observer decorator

I’d like to finish off with my favourite part of this post. Despite managing to simplify the notify() implementation with topic-based filtering, we still have to downcast and check each message before we can access the message-specific interface. There are, after all, only so many times you can type “blah equals dynamic cast to const foo pointer from bar, if blah not null then…” before your fingers will seize up and that’ll be the end of your programming days.

I’ll create an observer ‘decorator’ which will both wrap an observer and look like one. When notified, it will downcast the message to the type expected by the observer and forward it on.
First we need a generalised observer type templated on the message type. C++ doesn’t support template typedefs, so we employ a common workaround as discussed here by Herb Sutter. (NB: C++0x does support template typedefs under the name ‘template aliases’.) Then we can redefine observer in terms of the generalised type.

template<class message_type>
struct generic_observer
{
    typedef boost::function<void (const message_type&)> type;
};

typedef generic_observer<message>::type observer;

We don’t need to modify the subject definition as it will continue to store instances of observer. Instead, we now define the decorator which does the downcast. Here it is:

template<class message_type>
struct downcasting_observer
{
    typedef typename generic_observer<message_type>::type wrapped_type;

    downcasting_observer(const wrapped_type& observer)
        : obs(observer)
    {}


    void operator()(const message& m)
    {
        const message_type* p = dynamic_cast<const message_type*>(&m);
        if (p)
            obs(*p);
    }

private:
    wrapped_type obs;

};

Here’s a full demo program with all of the above rolled in:

#include <string>
#include <map>
#include <iostream>
#include <vector>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/foreach.hpp>

// ------- MESSAGE -------
struct message
{
    virtual ~message() {}

    typedef std::string message_id;
    virtual message_id id() const = 0;
};


// ------- OBSERVER -------
template<class message_type>
struct generic_observer
{
    typedef boost::function<void (const message_type&)> type;
};

typedef generic_observer<message>::type observer;


// ------- DOWNCASTING OBSERVER -------
template<class message_type>
struct downcasting_observer
{
    typedef typename generic_observer<message_type>::type wrapped_type;

    downcasting_observer(const wrapped_type& observer)
        : obs(observer)
    {}


    void operator()(const message& m)
    {
        const message_type* p = dynamic_cast<const message_type*>(&m);
        if (p)
            obs(*p);
    }

private:
    wrapped_type obs;
};



// ------- SUBJECT -------
struct subject
{
    typedef message::message_id message_id;

    void registerObserver(const message_id& id, const observer& o)
    {
        observers[id].push_back(o);
    }


    void notifyObservers(const message& m)
    {
        BOOST_FOREACH (const observer& o, observers[m.id()])
            o(m);
    }

private:
    typedef std::vector<observer> observer_set;
    typedef std::map<message_id, observer_set> observer_map;
    observer_map observers;
};



// ------- USAGE DEMO -------
struct foo_updated: public message
{
    foo_updated(int foo)
        : foo(foo)
    {}

    message_id id() const
    { return MESSAGE_ID; }

    const int foo;
    static const message_id MESSAGE_ID;
};
const message::message_id foo_updated::MESSAGE_ID("foo_updated");


void notify(const foo_updated& m)
{
   std::cout << "foo was updated to " << m.foo << std::endl;
}


int main()
{
    subject s;
    s.registerObserver(foo_updated::MESSAGE_ID, downcasting_observer<foo_updated>(notify));
    foo_updated m(42);
    s.notifyObservers(m);
}

You can see that the subject usage there doesn’t have to have any control structures to determine what it’s been given or to downcast it. All of those decisions are hidden behind the filtering enabled by the observer_map instance and the downcasting_observer.

Double dispatch as an alternative

An alternative mechanism to the decorator could be to employ ‘double dispatch‘, with the subject instead calling “message::notifyObserver(observer&)”, and the message then passing itself to the observer with its own type, rather than the base. To achieve this, message would have to be a template class, and an additional, non-templated base for message<T> would be required to store instances in a container together. Finally, this base would need to have one overloaded notify() method per message type.
Whilst this technique can be useful, I’m not a fan of this proliferation of types into the base, and as such don’t often use it. It would be particularly impractical for an API to need updating every time the application developer wanted to add a new message type.

Wrap-up

This post became rather larger than I’d hoped, so I hope it’s still digestible. The decorator pattern is a great one for extending functionality without modifying the wrapped object, and in a way this really exemplifies the ideal mechanism of extending functionality in an OO environment. Though C++0x does away with the need for the template typedef workaround, not everybody will have the luxury of using it and it’s a good one to have up your sleeve.

The next post will be a shorter one; I’ll be looking at the message types a little more closely, and show how we can use the ‘Curiously Recurring Template Pattern’ (CRTP) to prevent repetitive boilerplate in them. Happy coding!

Categories: Uncategorized Tags: , ,
Follow

Get every new post delivered to your Inbox.