Wednesday, August 22, 2012

C++11: A generic Singleton

1 Context

Yesterday, I found the solution to a problem that I was unable to solve one year ago. The problem was simple: Creating a generic Singleton. If you don't know what a Singleton is, here is the definition given in the book "Design Pattern" from the Gamma et al.: "Ensure a class only has one instance, and provide a global point of access to it".

I wanted to have a generic singleton because I had to code several singletons in several projects and I hate making the same thing several times. I was unable to succeed in my tries because of the initialization problem. There was no known method to handle the initialization that works for each possible constructors.

A perfect way to change a class into a Singleton would be, in my opinion, something like just inheriting from a base class Singleton. That would be awesome! But I don't think anyone has ever succeed. The best discussion about a generic Singleton I have read is in the Andrei Alexandrescu's book (If you know a better stuff about it, please let me know!). It notably talks about the deletion problem, that is not approached in this article. I just focus on the construction that can be improved thanks to C++11.

The common version is to use the Curiously Recurring Template Pattern (CRTP). So let's start by introducing the CRTP. The concept is simple, we inherit from a template class that takes us as parameter. It allows to make static polymorphism or instance counter… Wikipedia may help for these examples.

I had to say that when I discovered this thing I was amazed. I am passionated by the concept of the template, by the language in the language. One year ago I tried to solve the Singleton with a CRTP (which is the solution used in the C++ Modern Design book), and my specifications was the original one, and I had to call a method "destroySingleton" at the end of my programs. So I have to find a generic constructor that works for all of my Singleton classes and the work is done.

2 First Try

At the beginning, when I had only two classes and two constructors, I though to play on the fact that the template functions not used are not instantiated, so they can be incorrect (no type checking, no name binding… It just must be valid syntactically). Here is an example:

#include <string>

template <class T>
class Singleton
{
public:
  static
  T* get_instance(int v)
  {
    if (!instance_)
      instance_ = new T(v);

    return instance_;
  }

  static
  T* get_instance(std::string s)
  {
    if (!instance_)
      instance_ = new T(s);

    return instance_;
  } // Sounds familiar...

  static
  void destroy_instance()
  {
    delete instance_;
    instance_ = nullptr;
  }

private:
  static T* instance_;
};

template <class T> T* Singleton<T>::instance_ = nullptr;

// The classes that will be singletons:

class Single: public Singleton<Single>
{
public:
  Single(std::string s): s_(s) {}

private:
  std::string s_;
};

class Map: public Singleton<Map>
{
public:
  Map(int scale): scale_(scale) {}

private:
  int scale_;
};

// How to create and destroy them:

int main()
{
  Single* s = Single::get_instance("Forever Alone");
  Map* m = Map::get_instance(42);

  // Use these singletons.

  Map::destroy_instance();
  Single::destroy_instance();
}

When we instantiate Singleton<Single> there is no constructor that takes an int (so the first get_instance is incorrect), and the program still compiles successfully. This is because the function is not called, so not instantiated, so there is no reason to wine for g++.

This version respects one half of the deal to have a singleton, but it is totally non-intrusive in the code of the classes transformed into singleton. To respect the second half ("only one instance"), we have to tweak a little. We have to make the constructors of the derived classes private, and to grant the friendship to the mother class. As a reminder, the friend keyword allows to give access to all of the protected/private methods to the friend class. This way, no user can create directly an object of the derived class, and we respect the specifications.

Now we respect the two part, but we are still unhappy with this. In fact, we have only two classes, and we had to create two get_instance, because we have two different constructors. Since the number of possible combination of arguments to give to the constructor of an object is infinite, our solution isn't suitable. And that's where I was stuck.

3 The Solution

And yesterday, I finally realized that the solution was just in one of my previous post about the C++11 mechanisms that allows to create emplace_back. The solution was the perfect forwarding! In fact, we just have to create a get_instance that forwards the arguments to the constructor. No repetition, fully generic. That's what I wanted to reach! Here is the final version of the Singleton class:

#include <iostream>

template <class T>
class Singleton
{
public:
  template <typename... Args>
  static
  T* get_instance(Args... args)
  {
    if (!instance_)
      {
        instance_ = new T(std::forward<Args>(args)...);
      }

    return instance_;
  }

  static
  void destroy_instance()
  {
    delete instance_;
    instance_ = nullptr;
  }

private:
  static T* instance_;
};

template <class T> T*  Singleton<T>::instance_ = nullptr;

class Map: public Singleton<Map>
{
  friend class Singleton<Map>;
private:
  Map(int size_x, int size_y): size_x_{size_x}, size_y_{size_y} {}

public:
  int size_x_;
  int size_y_;
};

int main()
{
  Map* m = Map::get_instance(4, 5);

  std::cout << m->size_y_ << std::endl; // Outputs 5.

  Map::destroy_instance();
}

As said above, we are not interested in the destruction problem. It is well-covered in the Alexandrescu's book, and I recommend you to read it if you want to see more on the subject. We create a destroy_instance method in the aim to do not leak our instance. The user must call it at the end of the program.

The real novelty of this, is the use of std::forward when creating the object. So we can give to it any class, and it will work. I take the example of a Map (for example for a game) which takes two arguments, but I hope it is clear for everyone that it works with any constructors.

Note that, I didn't write the copy and move constructors private, but they should be. I omit them only to gain some space in my post. For the same reason, I didn't write the class Single in the second example. But it is clear that it works.

Before concluding, I just want to show that the use of the metaprogramming here doesn't lead to a hideous error message by our compiler when not used correctly. Let's assume that we replace the call to get_instance with two arguments, by a call with no arguments. What happens? Here is the answer of g++ (4.6.1):

singleton.cc: In static member function 'static T* \
Singleton<T>::get_instance(Args ...) [with Args = {}, T = Map]':
singleton.cc:53:30:   instantiated from here
singleton.cc:16:9: erreur: no matching function for call to 'Map::Map()'
singleton.cc:16:9: note: candidates are:
singleton.cc:40:3: note: Map::Map(int, int)
singleton.cc:40:3: note:   candidate expects 2 arguments, 0 provided
singleton.cc:35:7: note: constexpr Map::Map(const Map&)
singleton.cc:35:7: note:   candidate expects 1 argument, 0 provided
singleton.cc:35:7: note: constexpr Map::Map(Map&&)
singleton.cc:35:7: note:   candidate expects 1 argument, 0 provided

The message is clear, and there is all the information needed to understand it, and to fix our mistake.

In this post I proposed the use of std::forward for building a generic singleton. I don't talk the deletion problem because for my personal use, I accept to call the destroy method.

But this is just one example to show how the C++11 can help programmers to build new things easily. I hope my article motivates you to experiment new things! Feel free to share your opinion in comments :-)

PS: I just realize that my current solution isn't perfect. In fact, we can't get the instance without giving a valid argument list (valid = there is a constructor that takes this list). So a call to get_instance without argument, which should return the instance leads to an error if there is no constructor that takes no argument. This is not really what we want. A fix would be to separate the initialization and the get_instance. But that doesn't invalidate what I wanted to demonstrate. So it's okay :)

14 comments:

  1. Awesome stuff! I have always wanted to solve this problem. Thanks for sharing.

    ReplyDelete
  2. This is a really fancy way of using a really crappy design pattern. Singletons are simply a global variable - If you must have a single global object, why bother with all the singleton code and just make a global object?

    ReplyDelete
    Replies
    1. There is two parts in the definition of the Singleton, it also ensures that there is only one instance of this class.

      If I took this example it is mainly because it is very classical. I don't have any particular affect for global variable / Singleton.

      Delete
  3. is necessary a virtual destructor in the class singleton ?

    ReplyDelete
    Replies
    1. No it's not.

      IMHO, since you call the destructor of the derived class, it's okay.

      If it appears that I'm wrong, let me know :)

      Delete
  4. Yes! Also, inside the virtual destructor your could have a final check to see if the instance has been destroyed, and if not emit a warning and destroy it.

    ReplyDelete
    Replies
    1. I don't understand what your advice. The virtual destructor will be called only if the pointer is deleted.

      What happens in the destroy_instance method. At this point, we can't do nothing (instance_ is not a nullptr yet).

      Maybe I miss your point?

      Delete
  5. what's the point of putting 'static' on a separate line?

    ReplyDelete
    Replies
    1. No point at all. I'm just strict with the 80 columns rules.

      The template, these arguments, 'static', the return type and the rest of the signature of the function don't fit on 80 columns. So I split the line. That's all.

      It's just a personal habit.

      Delete
  6. two thoughts:
    1. beware that your singleton is not thread safe
    2. I'm not sure this works if you include it in more than one translation unit

    ReplyDelete
    Replies
    1. This implementation isn't written to be thread safe. The aim of this post is just to show how you can forward parameters to enhance the genericity of some piece of code. I found that this example was good for that. It's not hard to make it thread safe, that's just not the point here.

      I don't know why it would be problematic?

      Delete
  7. This post was published on hacker new, so there were some comments on this site:
    http://news.ycombinator.com/item?id=4456835

    ReplyDelete
  8. Very elegant solution for Singleton :^)
    I adopt for my developments. I will look later how to combine it with the thread taking the approach of Andrei Alexandrescu.
    I will send you a version if I find the solution.

    ReplyDelete
  9. Nice article! What about using static std::shared_ptr ?

    ReplyDelete