Fun with C++: Singletons

I just realized that I’ve not posted anything technical in a long while. Here’s my effort to remedy the situation.
This is a modified version of the straightforward implementation of the “singleton” pattern (STFW yourself.)

Let me add this. I’m rather proud of this code, so I ask you please to give this code a read if you are even a little interested.

First the definition of the singleton (which I named Zingleton to be used in Zorvan) and the handle class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//==========================================================
 
#include <cassert>
 
//==========================================================
 
#define DEF_ZINGLETON(T)  \
        template<> T * Zingleton<T>::ms_theone = NULL; \
        template<> int Zingleton<T>::ms_refcount = 0
 
//==========================================================
 
template <typename T>
class ZingletonHandle
{
public :
  inline ZingletonHandle ();
  inline ~ZingletonHandle ();
 
  inline T const * operator -> () const {return m_ptr;}
  inline T * operator -> () {return m_ptr;}
 
  inline T const & operator * () const {return *m_ptr;}
  inline T & operator * () {return *m_ptr;}
 
protected :
  T * m_ptr;
};
 
//==========================================================
 
template <typename T>
class Zingleton
{
  friend class ZingletonHandle<T>;
 
public :
  static ZingletonHandle<T> getZingleton ()
  {
    return ZingletonHandle<T>();
  }
 
protected :
  Zingleton ()
  {
    assert ((NULL == ms_theone && ms_refcount == 0) ||
	        (NULL != ms_theone && ms_refcount > 0));
    assert ((NULL == ms_theone && ms_refcount == 0));
 
    ms_theone = static_cast<T *>(this);
    ms_refcount = 1;
  }
 
  ~Zingleton ()
  {
    --ms_refcount;
 
    if (ms_refcount <= 0)
    {
      ms_theone = NULL;
      ms_refcount = 0;
    }
  }
 
private :
  static T * zingletonAcquire ()
  {
    if (NULL == ms_theone)
    {
      assert (0 == ms_refcount);
      zingletonCreate ();
    }
 
    assert (ms_refcount >= 0);
    ++ms_refcount;
 
    return ms_theone;
  }
 
  static void zingletonRelease (T const * instance)
  {
    assert (instance == ms_theone);
    if (instance != ms_theone)
      return;
 
    assert (NULL != ms_theone);
 
    --ms_refcount;
    assert (ms_refcount >= 0);
 
    if (ms_refcount <= 0)
      zingletonDestroy ();
  }
 
  static void zingletonCreate ()
  {
    assert (NULL == ms_theone);
    if (NULL == ms_theone)
    {
      ms_theone = new T ();
      ms_refcount = 0;
    }
  }
 
  static void zingletonDestroy ()
  {
    if (NULL == ms_theone)
      return;
 
    assert (0 == ms_refcount);
    delete ms_theone;
    ms_refcount = 0;
  }
 
protected :
  static T * ms_theone;
  static int ms_refcount;
};
 
//==========================================================
 
template<typename T>
inline ZingletonHandle<T>::ZingletonHandle ()
  : m_ptr (Zingleton<T>::zingletonAcquire())
{
  assert (NULL != m_ptr);
}
 
//----------------------------------------------------------
 
template<typename T>
inline ZingletonHandle<T>::~ZingletonHandle ()
{
  assert (NULL != m_ptr);
  Zingleton<T>::zingletonRelease (m_ptr);
}
 
//==========================================================

I think the code is clear enough, so I’m not gonna explain it any more! Let me just say that the handle class is there as a specific kind of smart pointer.
Here’s one way to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//==========================================================
 
#include <iostream>
 
//==========================================================
 
// Either include or paste the above code snippet here.
 
//==========================================================
 
class TestZ : Zingleton<TestZ>  // "CRTP"? Say what?
{
public :
  TestZ () {cout << "+It's alive!" << endl;}
  ~TestZ () {cout << "+I'm dying here..." << endl;}
 
  void Print () {cout << "+Hello, world." << endl;}
};
 
//----------------------------------------------------------
 
DEF_ZINGLETON (TestZ);
 
//==========================================================
 
void use_zingleton ()
{
  cout << "-Start of use_zingleton()..." << endl;
 
  ZingletonHandle<TestZ> h;
  h->Print ();
 
  cout << "-End of use_zingleton()." << endl;
}
 
//----------------------------------------------------------
 
int main ()
{
  cout << "-Start of main()..." << endl;
 
  {
    cout << "-Entered inner scope..." << endl;
 
    ZingletonHandle<TestZ> h;
    h->Print ();
 
    use_zingleton ();
 
    cout << "-End of inner scope." << endl;
  }
 
  cout << "-End of main()." << endl;
  return 0;
}
 
//==========================================================

When (if) you run the above code, it should produce something like this:

1
2
3
4
5
6
7
8
9
10
-Start of main()...
-Entered inner scope...
+It's alive!
+Hello, world.
-Start of use_zingleton()...
+Hello, world.
-End of use_zingleton().
-End of inner scope.
+I'm dying here...
-End of main().

As you can see, only one instance of the TestZ class is created and it is destroyed only after all the handles are destroyed. Basically, the handles are objects that guarantee that the singleton is alive and valid in their lifetime.

Homework: There is at least one very serious bug in the above code (that I’m unaware of.) What is it? It’s not the thread-safety issues because I’m aware of that and may fix and post again (that’s a great opportunity to talk about policy-based design in C++, so it demands another post or two.

VN:F [1.9.13_1145]
Rating: 10.0/10 (7 votes cast)
VN:F [1.9.13_1145]
Rating: 0 (from 0 votes)
Fun with C++: Singletons, 10.0 out of 10 based on 7 ratings

9 Comments

  1. MatGill says:

    DEF_ZINGLETON (ZingletonHandle);

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  2. MatGill says:

    damn it! not again! it removes the \\ part. i remember we got stuck at fixing the problem of letting others put code in the comments.

    Anyhow I was wondering what would happen if I declared “ZingletonHandle [in angle brackets] TestZ” as zingelton.

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  3. MatGill says:

    Of course, i’ll need a “ZingletonHandle [in angle brackets] (ZingletonHandle [in angle brackets] TestZ)” to create an instance of “ZingletonHandle [in angle brackets] TestZ” and there we go.

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  4. yzt says:

    Let’s have a convention that curly braces (“{}”) mean angle brackets (“<>”) in comments that contain code. I don’t think there’s any real chance of ambiguouity, is there?
    And I’m a bit sleep-deprived right now (just crossed 24 hours, no coffee!) but I think your suggestion will trigger a compile error (perhaps an stack overflow in the compiler, or a recursion-depth-limit overflow at compile time.)
    Any suggestions of how to make it impossible to do that? Is it even possible? Is it really necessary if it results in a compile error? After all, the compiler’s there to keep the user from shooting herself in her own foot!

    VN:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  5. ahf says:

    Awesome, I can’t wait to Zingletonize a bunch of crappy Singletons in Zorvan ;) … how do you know there is a bug you are unaware of?

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  6. MatGill says:

    Hehe, stupid mistake of mine with the brackets. Anyways, why should it run into a compile error? There is no recursion going on. Defining ZH-TestZ to be zingelton will declare Z-ZH-TestZ which in turn gets friend with ZH-ZH-TestZ. So you can still instantiate a ZH-ZH-TestZ, get access to a singleton instance of ZH-TestZ, and using that get a singleton instance of TestZ. Now when the scope for the ZH-ZH-TestZ finishes, it will die, which will cause the instance of ZH-TestZ to die, which in turn will cause TestZ to die (I assume we already defined TestZ to be zingleton).

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  7. yzt says:

    @ahf:
    Because there is always a bug that the coder is unaware of!

    @MatGill:
    No recursion? Emmmm… I think that was the result of the sleep deprivation.

    VN:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  8. yzt says:

    Of course, the most important bug in the above code is the fact that handles don’t behave correctly when copied. I should either disallow copying or fix the problem.
    (Which makes more sense?)

    VN:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VN:F [1.9.13_1145]
    Rating: 0 (from 0 votes)
  9. MatGill says:

    Block copying and use an explicit function for replicating the handles. Maybe use a deep-copy copy constructor.

    VA:F [1.9.13_1145]
    Rating: 0.0/5 (0 votes cast)
    VA:F [1.9.13_1145]
    Rating: 0 (from 0 votes)

Leave a Reply