Design Patterns III

When I wrote yesterday that I recently had a conversation about design patterns, I skipped over that conversation’s contents, focussing instead on the result. Much of the talk centered around implementing design patterns using C++ template (meta-)programming, such that they are evaluated at compile-time rather than run-time.

If that previous sentence didn’t mean much to you, you may not want to read on.

Part of the reason we drifted off in that direction is because I was one of those people who learned about design patterns after having used them for years, so during the lectures I attended, trying to implement some in C++ templates was just about the only thing keeping me awake. Another reason is that pattern descriptions don’t necessarily document intent, that is the problem or class of problems that a pattern is intended to solve, but merely the way code is structured. And that may have some implications when comparing compile-time and run-time implementations of a given pattern.

Take for example the Abstract Factory pattern. Roughly paraphrased, it hides the creation of objects in a function, where the decision which exact object is to be created is based on the function’s parameters. The obvious intent is to keep that knowledge in one easily located and modified place, instead of scattering it all over the code base.

Here’s what it may look like in an abbreviated C++ form:

1
2
3
4
5
6
7
8
9
10
11
12
Base1 * factory1(int a)
{
  switch (a)
  {
    case 1:
      return new Derived1();
    case 2:
      return new Derived2();
    default:
      return new Base1();
   }
}

So let’s compare this to a compile-time version of an Abstract Factory. You’ve likely seen at least one such function, std::make_pair(). Here’s what such a function may look like:

1
2
3
4
5
template <typename T>
Base2<T> * factory2(T const & t)
{
  return new Base2<T>(t);
}

So what’s the difference between these two versions, other than obvious syntactical ones?