Alternatives to Singletons and Global Variables

Alternatives to Singletons and Global Variables

By Bob Schmidt

Overload, 23(126):9-13, April 2015


Global variables and Singletons are usually considered bad. Bob Schmidt summarises some alternatives.

Recently, I posted what I thought was a simple question to the ACCU general forum:

My current project has several objects that are instantiated by main, and then need to be available for use by other objects and non-member, non-friend functions [NMNF] at all levels of the call tree. They really do represent global functionality, determined at program startup, for which there will be only one instance per executable.

Current best practices hold that global variables are bad, as are singletons. C++ does not support aspect-oriented programming. I don’t want to have to pass these objects around in every constructor and non-member function call so that they will be available when and where they are needed.

In the past I have used a global pointer to an abstract base class, and assigned the pointer to the instantiated derived class to this global pointer right after the object was instantiated in main. A similar approach would be to have a class that owns the pointer to the derived class, which can be retrieved through a static function. Having the globals be pointers to abstract base classes makes the classes that use the globals easy to test, because a test or mock object can be used in place of the real derived object. (One problem I have with Singleton and Monostate objects in this context is the direct tie to the concrete class.)

My Google-fu has failed me in my search for alternatives. There are plenty of people out in the blogosphere willing to say that using globals and singletons is bad, but few offer any practical alternatives. I’m not against bending or breaking the rules – perhaps one of those ‘bad’ options really is the best way. Anyone have any suggestions? Am I over thinking this?

Motivation

My current customer has a legacy system whose real-time data acquisition components are mostly in C code. I have been advocating migrating to C++, and over the past several years I have written two large, stand-alone subsystems in C++. Interfaces to new field hardware provided an ideal opportunity to start using C++ more extensively, with the long-term goal of re-writing some, if not all, of the existing code.

The short term goals were more realistic: develop a basic framework for processes that conforms to the system’s current overall architecture, and reuse existing components, wrapping the C code in C++ interfaces where required or desirable. A lot of the existing C functions will remain NMNF functions with C linkage.

The particular case that prompted my question is the system’s existing inter-process communications (IPC) subsystem. The details aren’t important (and are proprietary); the important fact for this discussion is that each process has only one interface to the IPC subsystem, and that interface is always initialized in main. Reads from the IPC subsystem are isolated to the main processing thread. The send functions are called from wherever they are needed, in the main processing thread (in response to received data), or in one or more threads that process data from the field device.

I wanted to wrap the existing C code in a C++ interface. There have been discussions about replacing the existing IPC scheme with something else (such as WCF), and my goal was to create an interface that would allow any IPC scheme to be used via dependency injection.

The forum discussion

It turns out the question was not so simple after all.

I should point out that I had already done a lot of research on the subject. There are a lot of similar questions out there, and a seemingly unlimited number of people with an opinion. The vast majority of responses to these questions contained very similar answers, which boil down to ‘global variables are bad, singletons are bad, don’t do that’. OK, I knew that already. I was trying to find an idiomatic C++ alternative, one that didn’t require that I pass one or more parameters to every constructor and NMNF function just because it might be needed by the next object constructed or the next NMNF function called.

The first several answers to my question seemed reasonable enough. They can be summarized as, yes, global variables and singletons are bad when abused, but there are some times and places when they solve a problem, and can be used in very specific and limited circumstances. (I’ll call these people the pragmatists.) One respondent mentioned the Service Locator pattern, with which I was not familiar [ Fowler ]. It doesn’t really solve my problem, but it is another tool in the kit.

It wasn’t too long before the strict ‘never use’ opinion showed up, and was bolstered by several concurring opinions. (I’ll call these people the purists.) Then the pragmatists returned, and a spirited debate was held. I was content to read the points and counter-points as they arrived; I had asked the question in order to be educated on the subject, and figured the best way to learn was to keep my eyes open and my mouth closed. As with most of these things, responses trailed off, and I was left with the email trail.

Points and counter-points

I’m not going to spend a lot of ink trying to summarize the points made by the two sides in this debate. The good people that participated in the debate made their cases, and I doubt I can do them justice in a short summary. If you are interested in all of the details you can read the entire thread online [ACCU].

What follows is a summary of the arguments presented in the thread, along with some commentary of my own. Fair warning – for the most part, I find myself in the pragmatic camp.

Parameterize from Above (PfA)

The PfA pattern, introduced by Kevlin Henney in ‘The PfA Papers’ [Henney07a ], was the only alternative presented as an answer to my question. Unfortunately, its implementation is the one thing I didn’t really want to have to do – pass the object to every class and every NMNF function that needs it, or calls something that needs it. Currently I’m working in a code base where in some subsystems almost every routine has the same list of multiple parameters, and have experienced the joy of having to add a parameter to multiple function signatures in order to get a piece of data where I needed it, because the previous maintainer didn’t think it would be required any further down the call tree.

Context Object Pattern

A context object aggregates multiple, repeated parameters into one structure, so there needs to be only one common PfA parameter passed amongst functions and classes [Henney07b ]. I’ve used this pattern when refactoring code without knowing it was a named pattern (recall the comment above about my current project); passing one parameter is certainly easier to manage than passing many, but it’s still repetitive, and a source of mental noise for those functions that don’t need the information themselves but are simply passing it on down the call tree.

Defined dependencies

One reason given for using the PfA pattern is that it defines the dependencies of a class or NMNF function. I don’t find this reason all that compelling. Not all objects used by a class or a function can or will be defined as a parameter. There are other ways we declare that module A (and its classes and/or functions) is dependent on something from module B – include files and forward declarations are two that immediately come to mind.

Testing

The Singleton and Monostate patterns both deal with concrete objects, not abstract classes. The purists rightly point out that this makes code that use these patterns hard to test, because the functionality provided by the objects cannot be mocked. Using PfA , the argument goes, allows the concrete object to be passed from function to function as a pointer to the abstract class, allowing for the substitution of mock objects during testing. I agree with the goal, if not necessarily with the implementation.

Exceptions for logging

Logging is one of those cross-cutting concerns that aspect oriented program [IEEE ] was designed to address. Unfortunately, C++ does not support the aspect oriented paradigm. Several purists said that they will sometimes make an exception for logging objects, and use a Singleton or a global, while others were adamant about never going down that path.

Order of initialization

Order of initialization issues can be tricky, particularly with static and extern variables spread over multiple compilation units. This hasn’t been a problem for me in past situations where I have used global variables or Singleton s. The limited types of functionality provided by those objects made it possible to initialize or instantiate the objects in main , before any other code needed to be executed.

Multi-threading

Singleton s are problematic in multi-threaded environments when lazy initialization is used. The possibility that the Singleton ’s instance method can be called for the ‘first’ time by two threads simultaneously leads to a nasty race condition. The race condition is easily removed by calling the instance method in main prior to spawning any of the threads that use it. Instantiating an object in main and then passing it around using PfA eliminates the race condition in a similar manner.

Other than that one case, I can’t see where PfA makes multi-threading any easier or less prone to error. An object shared by multiple threads still has to be thread-aware, regardless of the method used to get the object into the thread.

Use of cin, cout, cerr, and clog

The use of the standard C++ I/O streams was offered up by the pragmatists as an example of objects that represent global state and are not handled using PfA . One respondent replied that “ Code using these is untestable ” and in his classes he teaches his students to “ only include <iostream> in the .cpp module where main() is defined and only use the globals there to pass them down the call chain. […] In all other modules only use <istream> and <ostream> which define the API but not the global objects (or sstream or fstream ).

Instance() method

Having to call an instance method to return a pointer or a reference to an object, instead of just instantiating the object, is an awkward part of the Singleton pattern. I don’t think this, by itself, is a sufficient reason to reject the use of the pattern, but it does add to the negative pile.

Introducing the Monostate NVI Proto-Pattern

Listings 1 through 5 contain my initial solution to the problem. In my one additional contribution to the forum thread I called it (with tongue firmly in cheek) “ … a cross between the Monostate pattern and the template method pattern and/or Herb Sutter’s Non-Virtual Interface Idiom, with a little pimpl added for good measure. ” [ Sutter01 ] The version presented here is refined from that initial attempt, and (hopefully) fixes the typos.

Listing 1 is a simple abstract base class, and Listing 2 shows a class derived from the base class. This is all standard stuff. The examples are extremely simple, since complexity here wouldn’t add anything to the discussion.

class abstract_base 
{
public:
 abstract_base () 
 {
 }
 virtual ~ abstract_base () 
 {
 }
 virtual void foo () = 0;
};
			
Listing 1
class concrete_derived : public abstract_base
{
public:
 concrete_derived ()
   : abstract_base ()
 {
 }
 ~concrete_derived ()
 {
 }
 virtual void foo () override
 {
  // DO SOMETHING USEFUL
 }
};
			
Listing 2

Listing 3 shows the new class. Its primary characteristic is a pair of constructors – one default, and one that takes a shared pointer to the abstract base class. The constructor that takes the parameter is used to tie the concrete derived object to the Monostate NVI container. The default constructor is used to gain access to the derived object. The class contains inline, non-virtual functions that call the virtual functions defined by the abstract base class interface. Because the non-virtual functions are inline, the function call is removed by the compiler, with just the virtual function call remaining.

class mono_nvi
{
public:
  explicit mono_nvi 
    ( std::shared_ptr< abstract_base > p ) 
  {
    if ( p == nullptr )
      throw ( something );
    if ( mp != nullptr )
      throw ( something_else );
    mp = p;
  }
  mono_nvi () 
  {
    if ( mp == nullptr )
      throw ( something );
  }
  inline void foo () 
  {
    mp->foo ();
  }
private:
  static std::shared_ptr< abstract_base > mp;
};
std::shared_ptr< abstract_base > mono_nvi::mp;
			
Listing 3

Listing 4 illustrates how the concrete object is created and tied to the Monostate NVI object. Listing 5 is an example of a function that uses the combined objects. The call to nvi.foo() in listing 5 calls foo() against the object p instantiated in main .

int main ( int argc, char* argv[] )
{
  std::shared_ptr< abstract_base > p 
    ( new concrete_derived );
  mono_nvi nvi ( p );
  top_layer_function ();
}
			
Listing 4
void nested_function ( void )
{
  mono_nvi nvi;
  nvi.foo ();
}
			
Listing 5

This new class is not a Singleton ; it does not create the object, and does not have an instance() method. It looks a little like a Monostate ; it maintains a shared pointer to the resource being managed.

I see several advantages to this solution. First, unlike PfA , I don’t have to worry about passing this information around. Second, like the Monostate pattern, the object is accessed through a standard constructor. Third, the proto-pattern accesses objects through their abstract base class interface, making it easy to mock the object for testing.

One disadvantage of this solution is having to maintain the extra class. I don’t consider this a big disadvantage. Interfaces are not supposed to change often. When they do change, you have to modify all of the derived classes to match the new interface. Under normal circumstances this requires N changes; this proto-pattern bumps that up to N +1. A bigger disadvantage is the lack of compiler support that indicates that the extra class needs to be changed in response to a change in the interface. Presumably, if the interface is changing, some code somewhere is going to call the new or modified function, prompting a change or addition to the extra class.

But what about extensibility?

At one point during the project that prompted this whole discussion, a new requirement was discussed: the program would use one derived class object to communicate with X, and another, different derived class object to communicate with Y. Coincidentally, during this article’s early review process one of the reviewers wrote: “ One question it might be worth adding in, if Bob hasn’t already got it listed, is whether the design allows for future change; for example you start with a requirement for one ‘X’ and then later on you need two of them... ” It was if someone was reading my mind. Spooky.

That requirement didn’t survive, but the question of how this might be accomplished remained. My first thought was to use templates, which presents a problem of its own. I’m not a strong template programmer. Most of what I do doesn’t require that level of generality, so the templates I have created tend to be very straightforward. So, full disclosure – it is likely that the templates presented here are not idiomatically fully formed. (There are no associated traits classes, for example.)

My first attempt at a template solution is shown in Listing 6. This version allows multiple instances of proto-pattern objects to exist, as long as the types used in the template specialization are different. That is one weakness – the types need to be different.

template< class T >
class mono_nvi_template
{
public:
  explicit mono_nvi_template 
    ( std::shared_ptr< T > p ) 
  {
    // SAME AS IN LISTING 3.
  }
  // DEFAULT CONSTRUCTOR AND FUNCTIONS DEFINED 
  // THE SAME AS IN LISTING 3.

private:
  static std::shared_ptr< T > mp;
};
template< class T > std::shared_ptr< T >
  mono_nvi_template< T >::mp;
			
Listing 6

This led to the code in Listing 7. I had no idea if this was idiomatic or not, but it worked. It looks ugly, but I find most template code to be at least mildly unattractive. The typedef s at the end of Listing 7 make the usage of the template easier. (In real life I would have used enumerations instead of the magic numbers.) Listing 8 illustrates how we can now create multiple objects of the same or differing types. Listing 9 shows how the new objects are used.

template< int T >
class mono_nvi_template
{
public:
  explicit mono_nvi_template 
    ( std::shared_ptr< abstract_base > p ) 
  {
    // SAME AS IN LISTING 3.
  }
  // DEFAULT CONSTRUCTOR AND FUNCTIONS DEFINED 
  // THE SAME AS IN LISTING 3.

private:
  static std::shared_ptr< abstract_base > mp;
};

template< int T > std::shared_ptr< abstract_base >
  mono_nvi_template< T >::mp;

typedef  mono_nvi_template < 1 >  mono_nvi_one;
typedef  mono_nvi_template < 2 >  mono_nvi_two;
typedef  mono_nvi_template < 3 >  mono_nvi_three;
			
Listing 7
int main ( int argc, char* argv[] )
{
  std::shared_ptr< abstract_base > p1 
    ( new concrete_derived_1 );         // NOTE
  std::shared_ptr< abstract_base > p2 
    ( new concrete_derived_2 );         // THE
  std::shared_ptr< abstract_base > p3 
    ( new concrete_derived_2 );         // TYPES

  mono_nvi_one   mnvi1 ( p1 ); // THESE ALL REFER
  mono_nvi_two   mnvi2 ( p2 ); // TO DIFFERENT
  mono_nvi_three mnvi3 ( p3 ); // OBJECTS OF TWO
                               // DIFFERENT TYPES
  top_layer_function ();
}
			
Listing 8
void nested_function ( void )
{
  mono_nvi_one mnvi1; // REFERS TO p1 IN LISTING 8
  mono_nvi_two mnvi2; // REFERS TO p2 IN LISTING 8
  mono_nvi_three mnvi3; // REFERS TO p3 
                        // IN LISTING 8
  // ETC.
}
			
Listing 9

At this point the article was submitted for another round of reviews. The reviewers pointed out that the way I was using the integer to specialize the template was not, in fact, idiomatic. I was pointed in the direction of tag dispatching , the use of empty structs whose purpose is to provide a type-safe name as a template parameter. The reviewers also recommended using std::make_shared to create the object and a shared pointer to it in one step [Meyers] .

Listing 10 shows the class template modified to use tag dispatching. It features two template parameters. The first typically will be the abstract base class. The second, when the default is not used, is the tag that allows two objects of the same type T . Listing 11 contains examples of creating three distinct objects, similar to those created in listing 8.

template< class T, class TAG = void >
class mono_nvi_template
{
public:
  explicit mono_nvi_template 
    ( std::shared_ptr< T > p ) 
  {
    // SAME AS IN LISTING 3.
  }
  // DEFAULT CONSTRUCTOR AND FUNCTIONS 
  // DEFINED THE SAME AS IN LISTING 3.
private:
  static std::shared_ptr< T > mp;
};
template< class T, class TAG >
  std::shared_ptr< T > mono_nvi_template 
    < T, TAG >::mp;
			
Listing 10
struct mono_nvi_two {};   // THESE ARE THE TAGS
struct mono_nvi_three {};

mono_nvi_template < abstract_base > mnvi1
  ( std::make_shared< concrete_derived_1 > () );
mono_nvi_template 
  < abstract_base, mono_nvi_two > mnvi2 
  ( std::make_shared< concrete_derived_2 > () );
mono_nvi_template 
  < abstract_base, mono_nvi_three > mnvi3
  ( std::make_shared< concrete_derived_2 > () );
			
Listing 11

Wrap-up

My original solution was satisfactory; it provided the ease-of-use and testability I was looking for. (This is the format of the solution used in the first iteration of my client’s production code.) The final template version, prompted by an abandoned requirement and an astute reviewer (thank you), with further refinements provided by several reviewers, provides a more flexible solution.

Acknowledgements

I would like to thank all of you who participated in the thread. In the order in which you made your first comments, you are: Fernando Cacciola, Anna-Jayne Metcalfe, Alison Lloyd, Balog Pal, Pete Barber, Daire Stockdale, Aaron Ridout, Jonathan Wakely, Russel Winder, Thomas Hawtin, Giovanni Asproni, Martin Moene, Andrew Sutton, Kris, Paul Smith, Peter Sommerlad, and Hubert Mathews. Collectively you deserve credit for anything I got right this month. Any mistakes I made are my own.

As always, thanks also to Fran and the reviewers. This is my first attempt at writing about a technical subject, with real code that needed to compile correctly, and their encouragement and input were invaluable. As Fran stated in her article last month, “ (the reviewers) might be able to give a few pointers […] or other ways of doing things. [Buontempo15 ] I certainly learned several new ways of doing things, and for that I am grateful.

Thanks also to Michael Chiew and Larry Jankiewicz, who provided feedback during this idea’s early development.

An opposing opinion

One reviewer pointed out that this solution is still a global in disguise, with the usual downsides (I agree). He or she asked the rhetorical question, is it that much better than a simple global with get/set to do the checking?

unique_ptr< abstract_base > global_base;

void set_base 
  ( unique_ptr< abstract_base >  new_base )
{
  global_base = new_base;
  if ( global_base == nullptr ) 
      throw ( something );
}

abstract_base& get_base ( void )
{
  if ( global_base == nullptr )
    throw ( something );
  return *global_base;
}

void  using_function ( … )
{
  get_base ().foo ();
}

On the plus side the reviewer noted that my solution allows for substitutability and better controlled access than a global, and gets closer to having a template generate a lot of the boiler-plate.

One issue I see with this approach is one that the Singleton has – a non-standard way of getting the object. In this case, it’s a call to get_base() , as opposed to the instance() static member function common to Singleton s.

Corrections

There is an error in the print edition of my article in Overload 125, ‘I Like Whitespace’. The error was discovered by Martin Moene while he was preparing the article for the online edition. I’ll let him describe what he found (from his email to me):

“As web editor, I already have seen Overload 125 with your article ‘I Like Whitespace’. In it you have the [example at the bottom of the right-hand-column on page 14] featuring a ‘dangling else’. To me there’s a cognitive disconnect in the corrected version between function name process_x_is_0 and value of x for which it is invoked (!0) . I.e. the non-braced version does what it says, whereas the second does not. (In C and C++, else is associated with the nearest if .)”

Martin is, of course, correct. My example was in error. The name of the function called in the dangling else should have been process_x_is_not_0 . The online version of the code has been corrected. My thanks to Martin for discovering the error and publishing the corrected version online, and Alison Peck for the extra work she performed supplying the corrected version to Martin.

There also is a typo (yeah, I’m going with typo) in the complex Boolean expression at the top of the left-hand column on page 14 – an open parenthesis is missing before the subexpression z == 6 . This was pointed out to me by astute reader Jim Dailey, who also shared his preferred style for messy tests:

if (    (    ( x == 0 )
          && ( x == 1 )
        )
     || (    ( y == 3 )
          && (    ( z == 5 )
               || ( z == 6 )
             )
        )
   )

I thank Jim for pointing out my error, and sharing his style.

I regret the errors and any confusion they might have caused.

Bob

References

[ACCU] accu-general Mailing List, http://lists.accu.org/mailman/private/accu-general/2015-January/046003.html

[Buontempo15] Buontempo, Frances, ‘How to Write an Article’, Overload 125, February 2015

[Fowler] Fowler, Martin, ‘Inversion of Control Containers and the Dependency Injection Pattern’, http://martinfowler.com/articles/injection.html#UsingAServiceLocator

[Henney07a] Henney, Kevlin, ‘The PfA Papers: From the Top’, Overload 80, August 2007

[Henney07b] Henney, Kevlin, ‘The PfA Papers: Context Matters’, Overload 82, December 2007

[IEEE] Various authors, IEEE Software , January/February 2006, vol. 23

[Meyers] Meyers, Scott, Effective Modern C++ , O’Reilly, Item 21, p. 139

[Sutter01] Sutter, Herb, ‘Virtuality’, C/C++ Users Journal , 19(9), September 2001






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.