A pattern that I've been using for many years now is that of "singleton environments". I will illustrate the concept with some code, but the code hasn't been compiled (thus, might have typos).

The idea is that you might want to run more than one server, and more than one client, in a testing environment, but you want them all to run within the same process for easy debugging. This comes up especially when developing networked games.

You can't just make everything a traditional singleton at that point, because the different executing instances (say, two zone servers and two client windows) would step on each other.

Instead, the concept of an execution environment as a singleton factory can be used. For each service that you want to be able to expose to parts of your program (server, client, or both), set up a factory which knows how to create an instance of that service, and then vector through a map from service type to instance, and have the map create the service on first access.

In addition, you can make environments that are decorators on other environments, returning their own instances where no instance is present in the base environment.

This way, you create one execution environment per logical program instance you want to run, and configure your program objects with this environment. The program will use this environment, and all services created out of that environment will, in turn, use the environment for their needed services. This keeps everything compartmentalized, letting you run multiple instances of "applications" within the single process.

For some services, like file system and event scheduling, you might want to use the same instance for all the program instances; you can accomplish this by configuring the environment up-front with these instances.

The pattern in usage looks a bit like:

    void main()
    {
      IEnvironment * env = NewEnvironment();
      IEventScheduler * es = NewEventScheduler(env);
      env->setSingleton<IEventScheduler>( es );
      IFileSystem * fs = NewFilesystem(env);
      env->setSingleton<IFileSystem>( fs );

      IEnvironment * lsEnv = NewInheritedEnvironment( env );
      LoginServer * ls = new LoginServer( lsEnv, LOGIN_SERVER_PORT );
      IEnvironment * z1Env = NewInheritedEnvironment( env );
      ZoneServer * zs1 = new ZoneServer( z1Env, ZONE1_SERVER_PORT, "zone1.data" );
      IEnvironment * z2Env = NewInheritedEnvironment( env );
      ZoneServer * zs2 = new ZoneServer( z2Env, ZONE2_SERVER_PORT, "zone2.data" );
      IEnvironment * c1Env = NewInheritedEnvironment( env );
      Client * c1 = new Client( c1Env, "127.0.0.1", LOGIN_SERVER_PORT );
      IEnvironment * c2Env = NewInheritedEnvironment( env );
      Client * c2 = new Client( c2Env, "127.0.0.1", LOGIN_SERVER_PORT );

      es->runEventLoop();
    }
  

Using Execution Environments

Let's assume that LoginServer needs an IEventScheduler, an INetwork and an IDatabase. Its constructor would look something like:

    LoginServer::LoginServer( IEnvironment * env, short port )
    {
      eventScheduler_ = env->getSingleton<IEventScheduler>();
      network_ = env->getSingleton<INetwork>();
      database_ = env->getSingleton<IDatabase>();
      network_->startListening( port, this ); // call me when you get new connections
      eventScheduler_->addTimedEvent( this, 0.1 ); // run 10 times a second
    }
  

Clients of Execution Environments

Similarly, the ZoneServer and Client instances would get the services they need out of their respective environments. The service instances (except for the manually created services) get automatically created on demand, and initialized with the environment that the instance belongs to. Thus, an IDatabase service might actually want to use an INetwork and an IFileSystem internally, and get that from the environment.

To implement a service, you'd implement its interface, and have a constructor that can be called by the environment.

    class DatabaseImpl : public IDatabase {
      public:
        DatabaseImpl( IEnvironment * env );
        static ServiceFactory< IDatabase, DatabaseImpl > factory_;
    };

    ServiceFactory< IDatabase, DatabaseImpl > DatabaseImpl::factory_;
  

Service Implementation

This code assumes that the interface for IDatabase is an abstract base class that lives in some header, and DatabaseImpl actually implements that interface as a service, and the class declaration for DatabaseImpl actually lives in a cpp file (not a header file) because only the service factory needs the concrete class. This pattern will be used throughout the rest of the code: concrete classes go in source files; abstract classes, and templates go in headers.

So, the implementation of IEnvironment and of ServiceFactory is left. I think you can actually figure those out for yourselves, but I'll sketch them out:

    class IServiceFactory {
      public:
        virtual void * manufacture( IEnvironment * env ) = 0;
    };

    template< class Intf, class Impl >
    class ServiceFactory : public IServiceFactory {
      public:
        ServiceFactory() {
          IEnvironment::registerFactory( typeid(Intf).name(), this );
        }
        void * manufacture( IEnvironment * env ) {
          return new Impl( env );
        }
    };

  

Service Factory template

Note that the cast through void* looks scary, but the fact that the execution environment, in turn, also uses templates makes it type safe (enough).

    class IEnvironment {
      public:
        template< class T >
        T * getSingleton() { return (T *)getSingleton( typeid(T).name() ); }
        template< class T >
        T * setSingleton( T * inst ) { setSingleton( typeid(T).name(), inst ); }
        void registerFactory( char const * name, IServiceFactory * fac );
      protected:
        virtual void * getSingleton( char const * name ) = 0;
        virtual void setSingleton( char const * name, void * inst ) = 0;
    };

    class Environment : public IEnvironment {
      public:
        void * getSingleton( char const * name ) {
          std::map< std::string, void * >::iterator ptr = map_.find( name );
          if( ptr == map_.end() ) {
            void * inst = makeInstance( name );
            map_[name] = inst;
            return inst;
          }
          return (*ptr).second;
        }
        void setSingleton( char const * name, void * inst ) {
          std::map< std::string, void * >::iterator ptr = map_.find( name );
          if( ptr != map_.end() ) {
            throw std::runtime_error( "attempt to set a singleton twice" );
          }
          map_[name] = inst;
        }
        void * makeInstance( char const * name ) {
          std::map< std::string, IServiceFactory * >::iterator ptr =
              factories_.find( name );
          if( ptr == factories_.end() ) {
            throw std::runtime_error( "attempt to manufacture a service that does not exist" );
          }
          return (*ptr).second->manufacture( this );
        }
        std::map< std::string, void * > map_;
        static std::map< std::string, IServiceFactory * > factories_;
    };

    std::map< std::string, IServiceFactory * > Environment::factories_

    void IEnvironment::registerFactory( char const * name, IServiceFactory * fac )
    {
      std::map< std::string, IServiceFactory * >::iterator ptr = 
      Environment::factories_.find( name );
      if( ptr != Environment::factories_.end() ) {
        throw std::runtime_error( "attempt to register two factories for the same interface" );
      }
      factories_[name] = fac;
    }

  

Environment Implementation

The service factories get registered during static program initialization, before main() is executed, so you cannot use an environment until you get to main(). Typically, that's a pretty good rule to use anyway: doing too much work before main() leads to very hard to maintain and debug code.

So, there you have it. It's really quite a powerful way of managing what environment each piece of code executes within, without having to resort to process-global singleton instances. The minor overhead of looking up a service can usually just be paid for when you don't need to do it all that often, and if you do it often enough to matter, you will get the value into a local member variable, which is more efficient than the global-singleton pattern anyway (because it's branch-less and function-call-less).