mr-edd.co.uk :: horsing around with the C++ programming language

The morph-o-matic pattern

[20th April 2007]

I keep coming across this same pattern again and again, so much so that I suspect it's already been named by someone and I've simply failed to find its name (please let me know if that's the case!). It's used in a tremendously wide variety of powerful C++ kit. In fact here are the places where I can recall having used it in my own work and projects:

In fact the reason this pattern may not have a name is because I imagine it's really rather C++ centric. Certainly you wouldn't need this in your average dynamically typed language and I'm not sure it's even possible using Java or C# generics, each of which has a number of key differences to the C++ templates model.

In my head, I tend to refer to is as morph-o-matic, so that's what I'll call it here unless someone can point me to its actual name.

Anyway, I really wanted to make a post where I introduce this pattern with some relatively simple examples because I just know that a lot of the stuff I'll be talking about in future writings will make use of it and I feel it would be nice to have a page that I can send people to for reference. On top of that, it's just an incredibly useful technique to have in your box of tricks because it helps make code significantly shorter, clearer and more correct, as we'll see.

So what is the morph-o-matic pattern?

It seems to skulk in the shadows until the worlds of C++ templates and virtual functions collide; It's often the case that you have a bunch of classes that each have an interface element (member function, operator, associated free function, etc) with the same name and using that facility will perform comparative tasks for the classes they are associated with. However, these objects aren't related by inheritance and yet you would still like to treat them polymorphically at runtime.

Sure we can make a template function and employ the compile time polymorphism on offer there, but what if we wanted to put these objects in to a (homogeneous) STL container? For that we really need to have these objects related by inheritance and have a container of pointers (smart pointers, probably). So we must identify their compile time similarities and turn it in to runtime polymorphism, somehow.

That explanation may have created more questions than it answered. Hopefully an example or two will clear things up.

Example 1: polymorphic display

Let's say that we have a bunch of objects that we will have to write to an std::ostream at some point in the future. However, we don't know how far in the future, or where this stream is going to come from. What's more, the objects we're interested in have a variety of types so we can't simply add them to a container.

We decide to bite the bullet and create a base class from which we can inherit to implement the stream-able behaviour for all the types we're interested in.

// Classes derived from this should hold an object of some type and
// dump it to the stream given when write() is called.
class writable_base
{
    public:
        typedef std::auto_ptr<const writable_base> ptr_type;

        writable_base() { }
        virtual ~writable_base() { }
        virtual std::ostream &write(std::ostream &out) const = 0;
        virtual ptr_type clone() const = 0;

    private:
        writable_base(const writable_base &);
        writable_base &operator= (const writable_base &);
};

After a deep sigh, we start writing derived classes, each of which will implement the pure virtual functions to write() values of different type to a given ostream.

class writable_int : public writable_base
{
    public:
        writable_int(int i) : i_(i) { }
        std::ostream &write(std::ostream &out) const
        {
            return out << i_;
        }

        ptr_type clone() const
        {
            return ptr_type(new writable_int(i_));
        }

    private:
        int i_;
};

class writable_string : public writable_base
{
    public:
        writable_string(const std::string &s) : s_(s) { }

        std::ostream &write(std::ostream &out) const
        {
            return out << s_;
        }

        ptr_type clone() const
        {
            return ptr_type(new writable_string(s_));
        }

    private:
        std::string s_;
};

You pause for a moment to reflect on the monotiny of it all and then it hits you, like taekwondo-to-the-head! writable_int and writable_string have an identical structure. All that differs is the type we're printing. And the same will be true for writable_double and writable_widget too! The code is screaming templates at you!

You scrap the two classes you've just made and the numerous others that were lurking on the horizon, replacing them all with a single template class.

template<typename T>
class writable_impl : public writable_base
{
    public:
        writable_impl(const T &x) : x_(x) { }

        std::ostream &write(std::ostream &out) const
        {
            return out << x_;
        }

        ptr_type clone() const
        {
            return ptr_type(new writable_impl(x_));
        }

    private:
        T x_;
};

Good. Now writable_impl<int>, writable_impl<string> and all the other writable_impls derive from the same base class and can be used polymorphically. This is because each template instantiation of the writable_impl<> template class derives from the same base class, namely writable_base, which isn't a template. We've adapted the compile time polymorphism of operator<< in to a runtime polymorphic hierarchy.

To go one stage further, we decide to implement a wrapper that gives writable_impl nice value semantics and to make it's use even easier (you knew there was a reason you didn't call the base class writable in the first place!).

class writable
{
    public:
        template<typename T>
        writable(const T &obj) :
            impl_(new writable_impl<const T>(obj))
        { }

        std::ostream &write(std::ostream &out) const
        {
            return impl_->write(out);
        }

        writable(const writable &other) :
            impl_(other.impl_->clone())
        { }

        writable &operator= (const writable &other)
        {
            impl_ = other.impl_->clone();
            return *this;
        }

    private:
        writable_base::ptr_type impl_;
};

inline std::ostream &operator<< (std::ostream &out, const writable &w)
{
    return w.write(out);
}

That template constructor is pretty nifty. It now means we can write code such as:

std::vector<writable> objects;

objects.push_back(2);
objects.push_back(std::string("Hello!")); // *
objects.push_back(123.456);

for (unsigned i = 0, n = objects.size(); i != n; ++i)
    std::cout << objects[i] << '\\n';

A warm fuzzy feeling is felt all over. Though you're wondering why you decided to wrap "Hello!" in a std::string constructor on the line labelled *. But in your infinite wisdom, you decide to leave that as an exercise for the reader ;)

Example 2: exception safe resource management (RAII)

Hopefully you're already familiar with RAII in general and std::auto_ptr<> in particular. If not, read about them now in a C++ reference book, else this example isn't going to mean anything to you! It's probably the most important resource management technique used in C++[1].

Sometimes I wish that auto_ptr<> could handle resources other than memory, much like we can do with shared_ptr<> (from Boost and coming to a C++ standard library near you soon, if not already there):

shared_ptr<FILE> f(fopen("some_file.txt", "r"), fclose);

// do some stuff with f.
// fclose() will be called in f's destructor

Here, fclose() will be called in f's destructor, whether invoked due to normal flow of execution, or to an exception being propagated out of the scope.

Now shared_ptr<> is copyable, which isn't necessarily bad, but we might be in a situation where we don't want to allow copying for belt-and-suspenders compile time safety. If we were only managing memory, we could use scoped_ptr<> and scoped_array<>, but we're interested in general resources which may need cleaning-up using an arbitrary functor. So we're going to have to create our own utility.

Here's the first take. We have a template class with two type parameters. The first is the type of the resource we wish to manage and the second is the type of the clean-up functor.

template<typename T, typename Functor>
class scoped
{
    public:
        scoped(const T &res, const Functor &janitor) try :
            res_(res),
            janitor_(janitor)
        {
        }
        catch(...)
        {
            janitor(res); // guarantee cleanup
            throw;
        }

        ~scoped() { janitor_(res_); }

        T get() { return res_; }

    private:
        scoped(const scoped &);
        scoped &operator= (const scoped &);

    private:
        T res_;
        Functor janitor_;
};

This might look a bit peculiar if you haven't seen a constructor-try block before now, in which case look it up in your favourite C++ book. It basically allows us to handle exceptions that occur when running through the initializer list, before letting the exception out in to the wild.

But this thing will be a bit of a pig to use. For example, the previous FILE based example would now look like:

scoped<FILE *, int (*)(FILE *)> f(fopen("some_file.txt", "r"), fclose); // yucky function-pointer type specificied!

// do some stuff with f.
// fclose() will be called in f's destructor

That's pretty ugly. Of course, we could deduce the type of clean-up function-pointer from T if we wanted to. But this would seriously restrict what could be done with the facility. For instance, it would mean that we would not be unable to use function-objects as a clean-up mechanism. Similarly the ability to use function-pointers that didn't have a return type of int would be thrown out the window.

So it really is desirable to keep the flexibility given by allowing arbitrary clean-up functor types. Luckily we can do better in terms of ease-of-use. We can change the code so that scoped<> only has a single template parameter and the construction is done in the same way as with shared_ptr<>.

The down side is that we'll have to sacrifice a little performance because we'll end up doing some dynamic allocation. This isn't a biggie as far as I'm concerned (in fact there are some nifty delegate implementations floating about that would alleviate this problem, too). Anyway, the point is that we get to see this pattern in action again:

template<typename T>
class janitor_base
{
    public:
        virtual ~janitor_base() { }
        virtual void cleanup(T the_mess) = 0;
};

template<typename T, typename Functor>
class janitor : public janitor_base<T>
{
    public:
        janitor(const Functor &f) : f_(f) { }
        void cleanup(T the_mess) { f_(the_mess); }

    private:
        Functor f_;
};

template<typename T>
class scoped
{
    public:
        template<typename Functor>
        scoped(const T &res, Functor f) try :
            res_(res),
            janitor_(new janitor<T, Functor>(f))
        {
        }
        catch(...)
        {
            f(res); // guarantee cleanup
            throw;
        }

        ~scoped() { janitor_->cleanup(res_); }

        T get() { return res_; }

    private:
        scoped(const scoped &);
        scoped &operator= (const scoped &);

    private:
        T res_;
        std::auto_ptr<janitor_base<T> > janitor_;
};

There are a couple of things I should point out about scoped<>. Of course, it's not just applicable to FILE* and fclose(). It could also be used for such things as:

The only restriction is that calling the clean-up functor on the object given at construction is the same as calling a copy of the functor on a copy of resource. Typically a resource is represented by some opaque-but-actually-primitive type such as an integer or pointer, in which case we're fine.

What does morph-o-matic bring to the table?

I think morph-o-matic is pretty damn cool. Even the simple examples above illustrate that:

One might argue that the down side is the potential code-bloat due to all the class template instantiations involved. But the amount of code is the same, whether each derived class is written by hand, or by the compiler via template instantiation.

Anyway, now that I've introduced the pattern, I won't feel so uncomfortable using it in code in other posts!

Downloads

Footnotes
  1. Bjarne Stroustrup's The C++ Programming Language is a good source of information on RAII. I believe it was he that coined the phrase/acronym, too []

Comments

(optional)
(optional)
(required, hint)

Links can be added like [this one -> http://www.mr-edd.co.uk], to my homepage.
Phrases and blocks of code can be enclosed in {{{triple braces}}}.
Any HTML markup will be escaped.