0

I have implemented custom class representing an enumeration possibilities similarily to this article:

// Color.h

class Color {
public:
    static const Color Red;
    static const Color Green;
    static const Color Blue;
    //...
private:
    explicit Color(int code);
    //...

    int code;
    static std::set<Color> colors;
};

// Color.cpp:

const Color Color::Red(1);
const Color Color::Green(2);
const Color Color::Blue(3);    
//...

Now I have problem when I want to use instances of Color e.g.: Color::Red to initialize a global variable in another translation unit. I understand that it happens because it is not defined which translation unit global variables are initialized first. How the initialization ordering problem could be solved?

The only solution that I can think of is using nifty counter. But I am not able find out, how to use it without affecting syntax of the enumeration class. I was thinking about adding set() method into Color. Then I could call this method in a nifty counter like initializer:

// Color.h

class Color {
public:
    void set(int code);
    //...
private:
    Color() { /* empty */}
    //...
};

static class ColorInitializer {
    ColorInitializer () {
        static bool initialized = false;
        if(initialized)
            return;

        Color::Red.set(1);
        Color::Green.set(1);
        Color::Blue.set(1);
        initialized = true;
    }
} colorInitializer;

// Color.cpp

const Color Color::Red;
const Color Color::Green;
const Color Color::Blue;

But the problem I see here is that the set method could be called on object that is not yet constructed. Is that OK or is the behavior undefined? How to better solve the problem of undefined initialization order?

Juraj Blaho
  • 13,301
  • 7
  • 50
  • 96
  • I do not have experience with C++11, but I seriously suspect that the whole approach with `ColorInitializer` is not good. Better investigate using initialization with `{...}`, etc. What error are you getting for your fist code sample? – Kirill Kobelev Jul 12 '12 at 13:17
  • @Kirill Kobelev: I am not getting any compilation errors, it is only an undefined behavior. The variable may or may not be initialized when it is used. – Juraj Blaho Jul 12 '12 at 13:21
  • Then I would suspect that this behavior has no relation to your particular class. Try to isolate the problem. Something is wrong with linker? – Kirill Kobelev Jul 12 '12 at 13:25
  • The simplest way to guarantee the correct initialisation order is not to have any globals. – Mike Seymour Jul 12 '12 at 14:52
  • @Frerich Raabe: As you can see, my accept ratio is pretty high. That means I know how to accept an answer. – Juraj Blaho Jul 16 '12 at 07:40

3 Answers3

3

In C++11 (if you can afford using it) you could use extended constant expression functionality:

class Color {
public:
    static constexpr const Color Red;
    static constexpr const Color Green;
    static constexpr const Color Blue;
private:
    constexpr explicit Color(int code);
    //...
};

// Color.cpp:

constexpr Color Color::Red(1);
constexpr Color Color::Green(2);
constexpr Color Color::Blue(3);    
//...
Andrzej
  • 5,027
  • 27
  • 36
2

Don't use global objects in the first place. Instead, use functions:

class Color {
public:
    static const Color &Red() {
      static const Color c( 1 );
      return c;
    }

    static const Color &Green() {
      static const Color c( 2 );
      return c;
    }

    static const Color &Blue() {
      static const Color c( 3 );
      return c;
    }
private:
    explicit Color(int code);
    //...
};

You can now use these functions to initialize other objects; calling them will ensure that the constructor of the Color objects is called.

Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • Unfortunately, **this is not thread-safe on C++03** (although it is on C++11 and recent versions of gcc). See also: http://stackoverflow.com/questions/335369/finding-c-static-initialization-order-problems/335746#335746 – Cody Gray - on strike Jan 15 '16 at 13:29
2

Since your Colour objects are lightweight, just could just use static functions.

class Color {
public:
    static Color Red() { return Color(1); }
    static Color Green() { return Color(2); }
    static Color Blue() { return Color( 3 ); }
private:
    explicit Color(int code);
};

The only problem with this is you can not pass these objects, to functions expecting pointers or non-const references (but non-const references in this case is probably nonsensical anyway).

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187