Hacker News new | ask | show | jobs
by palish 5534 days ago
As a video game programmer, I use the Singleton pattern quite often. For example, "TextureMgr", "MaterialMgr", "ModelMgr", "GrSubsys" (for Graphics Subsystem), etc..

The most beautiful Singleton code I've seen in C++ is:

  class GrSubsys
  {
  public:
    GrSubsys();
    ~GrSubsys();
  };
  extern GrSubsys* gGrSubsys;

... then the constructor and destructor are written such that you can startup/shutdown the singleton as follows:

  //-------------------------------------------------------
  void
  App_Startup()
  {
    // initialize graphics subsystem.
    new GrSubsys;
  }

  //-------------------------------------------------------
  void
  App_Shutdown()
  {
    // shutdown the graphics subsystem.
    delete gGrSubsys;
  }

  //-------------------------------------------------------
  int
  main()
  {
    // startup the engine.
    AppStartup();

    // enter the per-frame application loop.
    while ( AppFrame() )
    {
    }

    // shutdown the engine.
    AppShutdown();

    return 0;
  } 
     
Shrug. A friend introduced it to me, and I liked it a lot. The same concept can be easily applied to C, too.
2 comments

We used a lot of singletons (also video game deveoper) in the past, to the point where the code is unreadable:

some_system::get_instance()->MemberFunc(), instead of sys->MemberFunc()

Problem came apparent when somebody had made the Camera singleton, and it was singleton...

Nowadays we have one keyboard, one mouse, one printer... We used to have one DISPLAY, even one WINDOW (fullscreen), and one JOYSTICK. Just to get the point...

how much unit testing do you do?
A better question might be, "Would this pattern impact our ability to write unit tests?" I believe the answer is "No."

Let's say the module Foo depends on the Graphics subsystem. That is, Foo.cpp has the code:

  #include "GrSubsys.h"

  //-------------------------------------------------------
  Foo::Foo( const string& name )
  {
    // fetch a handle to our model (loading it if necessary).
    _model = gGrSubsys->GetModel( "models/" + name );
  }

  //-------------------------------------------------------
  bool
  Foo::IsValid()
  {
    return ( _model != NULL );
  }
In order to write a unit test that takes into account the aforementioned Singleton pattern, you might write:

  //-------------------------------------------------------
  void
  Test_EngineComponents()
  {
    // prepare for science.
    AppStartup();
   
    //==========================
    // Test #1 - Foo
    //==========================
    {
      // load a Foo entity.
      Foo* sunTzu = new Foo( "test/warlord" );
    
      // verify the entity loaded successfully.
      assert( sunTzu->IsValid() );

      // shutdown.
      delete sunTzu;
    }

    // conclude our science.
    AppShutdown();
  }

and AppStartup() is the function which initializes the subsystem singletons (and those will initialize their manager singletons).

It's about discipline. Any fool can butcher with any tool.

Unless I have missed something about unit testing somewhere I am pretty sure that fails the "unit" part of unit testing. Unless that "#include GrSubsys.h" is actually importing a mock graphics subsystem.
A mock graphics subsystem isn't possible since it depends directly on the video card.
My initial point was that the code isn't really unit testable. From what I know, this is common in video games..too much code tied too hardware.

I'm certainly not saying this is bad. I am saying that, from my experience, video game programming is very different than other sorts of programming.

Ah. I gave an example of running a unit test against the hardware. I'd like to understand why it's not really testable? Not to defend myself -- to better myself.
Sorry fella but you don't understand the concept of unit testing. I suggest you do some reading on the subject.

http://en.wikipedia.org/wiki/Unit_testing

"In a unit test, mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test."

Why would you want to simulate the graphics subsystem (and therefore the video card)?

The goal here isn't performance testing. It's to check whether the engine works.