Juraj Blaho
Juraj Blaho

Reputation: 13451

Guaranteeing correct initialization order of global variables

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?

Upvotes: 0

Views: 1743

Answers (3)

Michael Anderson
Michael Anderson

Reputation: 73480

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).

Upvotes: 2

Frerich Raabe
Frerich Raabe

Reputation: 94299

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.

Upvotes: 2

Andrzej
Andrzej

Reputation: 5127

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);    
//...

Upvotes: 3

Related Questions