C++ - Initializing a static map as a private class member

22,142

Solution 1

#include <map>
#include "Color.h"

enum COLOR
{
    RED = 0, BLUE, GREEN, YELLOW, ORANGE, WHITE, BLACK,
    BRICKS_FROM_A_DISTANCE_ON_AN_UNUSUALLY_SUNNY_AFTERNOON,
    // etc
    COLOR_COUNT
};

class ColorManager
{
    typedef std::map<COLOR, Color> ColorMap;

public:
    ColorManager();
    Color getColor(COLOR color) const;

private:
    static ColorMap createColorMap();
    static ColorMap colorMap;
};

// in some .cpp file:

ColorManager::ColorMap ColorManager::createColorMap()
{
    ColorMap ret;
    // populate ret
    return ret;
}

ColorManager::ColorMap ColorManager::colorMap = ColorManager::createColorMap();

Or with C++11:

#include <map>
#include "Color.h"

enum COLOR
{
    RED = 0, BLUE, GREEN, YELLOW, ORANGE, WHITE, BLACK,
    BRICKS_FROM_A_DISTANCE_ON_AN_UNUSUALLY_SUNNY_AFTERNOON,
    // etc
    COLOR_COUNT
};

class ColorManager
{
    using ColorMap = std::map<COLOR, Color>;

public:
    ColorManager();
    Color getColor(COLOR color) const;

private:
    static ColorMap colorMap;
};

// in some .cpp file:

ColorManager::ColorMap ColorManager::colorMap = []
{
    ColorMap ret;
    // populate ret
    return ret;
}();

Solution 2

std::map has a constructor that takes a pair of iterators as arguments, so you could initialize the map with, for example, an array of pairs:

#include "Color.h"

#include <map>

enum COLOR { RED = 0, BLUE, GREEN, YELLOW, ORANGE, WHITE, BLACK,
    BRICKS_FROM_A_DISTANCE_ON_AN_UNUSUALLY_SUNNY_AFTERNOON,
    // etc
    COLOR_COUNT };

class ColorManager
{
public:
    ColorManager();
    ~ColorManager();
    Color getColor(COLOR color) const;
private:
    typedef std::map<COLOR, Color> ColorMap;
    static ColorMap colorMap;
};

using std::make_pair;
using std::pair;

std::pair<COLOR, Color> colorPairs[] = {make_pair(RED, Color(...)),
                                        make_pair(BLUE, Color(...)),
                                        make_pair(GREEN, Color(...)),
                                        ...};

ColorManager::ColorMap ColorManager::colorMap(colorPairs, colorPairs + COLOR_COUNT);

In C++0x, you will be able to simply do this:

ColorManager::ColorMap ColorManager::colorMap({{RED, Color(...)},
                                               {BLUE, Color(...)},
                                               {GREEN, Color(...)},
                                               ...});

Solution 3

use a static method which creates an initialized map:

ColorManager::colorMap(ColorManager::makeColorMap());

where makeColorMap is the following static method:

ColorManager::ColorMap ColorManager::makeColorMap()
{
  ColorMap retval;
  retval[...] = ...;
  retval[...] = ...;
  ...

  return retval; 
}

Solution 4

One option would be to change ColorMap from a typedef into its own custom type with a constructor that correctly initializes the map contents. That way, when you statically initialize the ColorMap, the constructor will fill it in with the proper data and any operations that try using the ColorManager will see the properly-configured ColorMap.

Solution 5

You could do it like this without the need of a class:

Color getColor(COLOR color)
{
      static std::map<COLOR, Color> colorMap;
      if(colorMap.empty()) // Only runs once.
      {
          colorMap[BLUE] = Color();
          // ... etc ...
      }

      return colorMap[color];
}
Share:
22,142

Related videos on Youtube

Shaun
Author by

Shaun

Updated on July 09, 2022

Comments

  • Shaun
    Shaun almost 2 years

    Let's say I was quite bored one late evening and after catatonically staring at the computer monitor for a few hours, I decided to implement an aggregate C++ class to manage colors for drawing pixels, because I've obviously gone mad. For starters, we'll just tell the (probably singleton) ColorManager object what color we want to use and it'll return a Color object, whatever that may be.

    A simple implementation:

    #include "Color.h"
    #include <map>
    
    enum COLOR { RED = 0, BLUE, GREEN, YELLOW, ORANGE, WHITE, BLACK,
        BRICKS_FROM_A_DISTANCE_ON_AN_UNUSUALLY_SUNNY_AFTERNOON,
        // etc
        COLOR_COUNT };
    
    class ColorManager
    {
    public:
        ColorManager();
        ~ColorManager();
        Color getColor(COLOR color) const;
    private:
        typedef std::map<COLOR, Color> ColorMap;
        static ColorMap colorMap;
    };
    

    So, hopefully, this simple code:

    ColorManger colorManager;
    Color blue = colorManager.getColor(BLUE);
    

    should make it real easy for us to do whatever nonsense for which you'd need Color objects.

    The problem is that you need to initialize your static private ColorMap somewhere so that each COLOR enum corresponds to a proper Color object, and VC++ 2010 doesn't seem to like anything you try. So the question is, how and where do I initialize this map?

    Obviously, this is a contrived example, but beyond that, perhaps defining static variables for a class that functions as a singleton is not worth the trouble. Or, perhaps, I might as well just declare the variable static inside of getColor() since that's the only function that uses it, and just incur the overhead the first time the function is called (although for this simple example, that's not much better than just putting a giant switch statement in there).

    Whatever the case, I appreciate the feedback.

  • Shaun
    Shaun almost 13 years
    In VS2010, this causes a runtime memory access violation in xtree, presumably when the values are assigned in the class constructor. It does compile though.
  • Shaun
    Shaun almost 13 years
    Indeed, I mentioned this method in my question. However, ColorManager is an analog for a much larger, much more complicated class with a lot more functionality, so I'm not looking to get rid of it.
  • Shaun
    Shaun almost 13 years
    I marked ildjarn's answer as accepted, but this is the same solution given at about the same time, so upvoted.
  • Andre Holzner
    Andre Holzner almost 13 years
    thanks ! I noticed that we concurrently were submitting similar solutions.
  • littleadv
    littleadv over 10 years
    @SHaun obviously you're doing something wrong in the constructor... Beware not to rely on other static variables.