Macro, with or without you (C++)

Macros are the bluntest instrument of C and C++’s abstraction
facilities, ravenous wolves in functions’ clothing, hard to tame,
marching to their own beat all over your scopes. Avoid them.

C++ Coding Standards: 101 Rules, Guidelines, and Best Practices
by Herb Sutter; Andrei Alexandrescu

Writing programs is not always easy, but in the most of cases, it is a lot of fun. (Note: reading other programmers’ code and talking about programming are the same, if you are open minded enough. When you are not alone and you are working with other guys, you face that neither you nor the world is perfect, but when you can live with that, it isn’t a problem.) But sometimes the fun is a little too much. You are fed up of feeling like your project never will be done or you are too week to fix the bugs before the deadline. If it happens to me, I just start to write interesting code and weird programs, because it helps me to remember why programming is so beautiful.

Function-like macros are not safe, function-like macros are ugly and function-like macros are dangerous, Herb Sutter and Andrei Alexandrescu are completely right. But function-like macros are interesting, because the macro substitution is completely different from the ordinary logic of programming. C++ is a sophisticated and complicated tool, if you want to use it, you need thinking in a sophisticated way. Macros are simple like hammers and when you want to solve complicated problems with macros, then you must be crafty, because you need to find a creative way to overcome the lack of the capabilities of the macros.

Okay, we need a creative task, we want to write macros. In these days, I’m working on small C++ project and I frequently use the singleton and the flyweight patterns. I must write the following code again and again:

class MyClass {
 public:
  static MyClass& instance() {
    static MyClass inst;
    return inst;
  }
 ...
};

It is a bit boring to write something again and again. (Do you remember the DRY principle?) So, how can we avoid the repetition? When I read the the paragraph about the copy constructor in the Google C++ Style Guide, the macro DISALLOW_COPY_AND_ASSIGN impressed me, because it seems so elegant. Could we express the singleton pattern in the same way? Of course we can.

#define SINGLETON(NAM) static NAM& instance() {static NAM inst; return inst;}

class MyClass {
 public:
  SINGLETON(MyClass);
 ...
};

However, it isn’t very good, because MyClass contains a member function (instance), that isn’t listed in the source files. (In other words, it makes the code less understandable.) And it isn’t very good, because there is a much better (and more crafty) implementation, which uses inheritance and a template class, see here. (Frankly, I just love write such class headers like class MyClass : Singleton<MyClass>. It seems so expressive.) I place the much better solution here, because it is so beautiful. (Note, that singleton classes are always uncopiable, it is a trivial attribute of singleton classes.)

template <class Derived>
class Singleton : Uncopiable //see the 'Google C++ Style Guide'
{
public:
   static Derived& getInstance()
   {
     static Derived instance;
     return instance;
   }
protected:
   Singleton() {}
};

class MyClass : public Singleton<MyClass>
{
   // original contents of TestClass
};

Compare the macro solution to the template solution. I think you feel, that the template solution is much better. Remember, inline template functions should be applied instead of function-like macros. (I think you know the famous min function, which can be implemented either via macros or via inline template function, the two different code will be compiled to the same binary executable file, but the template code is more safe and more maintainable.) This example suggests that we always can use templates instead of macros and they are not worse at all, and likely it is very close to the truth.

The flyweight pattern is more complicated than the singleton pattern. I tend to implement it in the following way. (Of course it need not to be said that I use a better hash function and collision checking, and I don’t use inline functions when it is meaningless.)

#ifndef MYCLASS_H
#define MYCLASS_H

#include <iostream>
#include <map>
#include <tr1/memory>

class MyClass {
 public:
  typedef std::map<int, std::tr1::shared_ptr<MyClass> > INSTANCEMAP;
  static INSTANCEMAP instances;

  static MyClass& instance(int a, int b, int c) {
    int key = instance_keyfunc(a, b, c);
    INSTANCEMAP::iterator it = instances.find(key);
    if (it != instances.end()) {
      listInstances();
      return (*((*it).second));
    } else {
      instances[key] = std::tr1::shared_ptr<MyClass>(new MyClass(a, b, c));
      listInstances();
      return *(instances[key]);
    }
  }

  static void listInstances() {
    //list instances
    std::cout << "List of instances: ";
    for (INSTANCEMAP::iterator mit = instances.begin();
         mit != instances.end();
         ++mit)
    {
      std::cout << (*mit).first << ";";
    }
    std::cout << std::endl;
  }

  void printABC() {
    std::cout << "{a = " << a << "; b = " << b << "; c = " << c << "}" << std::endl;
  }

 private:
  MyClass(int a, int b, int c) :
    a(a), b(b), c(c) {}

  static int instance_keyfunc(int a, int b, int c) {
    return a * 10000 + b * 100 + c;
  }

  int a;
  int b;
  int c;
};

MyClass::INSTANCEMAP MyClass::instances;

#endif

I started to simplify this code with macros, I wanted to write something like:

class MyClass {
public:
  FLYWEIGHT(MyClass, int, a, int, b, int, c);
  ...
};

A such macro is possible, but it isn’t easy, it uses other macros or a chain of macros. And after the last example, the singleton template, I don’t feel like writing a macro because a template must be much more cool. The hardest part of the flyweight pattern is the handling of the constructor parameters of the derived class, the constructor of the Flyweight template class gets only a hash value, and it uses only the hash value to distinguish the instances.

template <class Derived>
class Flyweight
{
 public:
  static Derived& getInstanceByHash(int hashValue);
 protected:
  Flyweight() {}
};

class MyClass : public FlyWeight<MyClass>
{
 pubilc:
  static MyClass& getInstance(int a, int b, int c) {
    return getInstanceByHash(hash_function(a, b, c));
  }
  static hash_function(int a, int b, int c);
 ...
 private:
  MyClass(int a, int b, int c) : a(a), b(b), c(c) {...}
 ...
};

Short and sweet, macros are not interesting, but they aren’t very useful. I think a C++ a real programmer doesn’t need to use macros and when he does it, he want to feel himself an oldscool C programmer. Likely templates are invented to make life more boring, but they are beautiful enough and that’s why we use them.

Advertisements

About vrichard86

I'm Richard and I'm an enthusiastic coder. I want to be better and I think a professional journal is a great self improvement tool. Welcome to my blog, to my hq, to my home on the web!
This entry was posted in C++ and tagged , , , , . Bookmark the permalink.