Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constructor ordering (global scope) issue

I have a constructor ordering issue that I am trying to come up with creative ways to solve.

Basically I have a simple class Color which stores RGB colour information and allows manipulation of said colour and conversion into other colour spaces (24 bit, 16 bit, 4 bit, HSV, XYZ, LAB, etc). The class itself is working perfectly.

I also have a library of pre-defined colours, such as:

namespace Colors {
    const Color Snow                  (255,250,250);
    const Color GhostWhite            (248,248,255);
    const Color WhiteSmoke            (245,245,245);
    const Color Gainsboro             (220,220,220);
    const Color FloralWhite           (255,250,240);
    const Color OldLace               (253,245,230);
    const Color Linen                 (250,240,230);
    const Color AntiqueWhite          (250,235,215);
    const Color PapayaWhip            (255,239,213);
    const Color BlanchedAlmond        (255,235,205);
};

And they all work fine too when used in a program normally.

My issue comes when I try to use those library colours in the constructor to another object. There is nothing to say that the constructor for the library colour I use has been executed and the colour data assigned (it does some small pre-processing to calculate some of the different colour space values) before the constructor for the other class that receives the Color object and assigns it to a storage variable inside itself.

For example, the Color class has a constructor:

Color(const Color &c) {
    setColor(c.getRed(), c.getGreen(), c.getBlue());
}

And an = operator:

Color &Color::operator=(const Color &rhs) {
    setColor(rhs.getRed(), rhs.getGreen(), rhs.getBlue());
    return *this;
}

setColor() is just a little helper function that stores the values and pre-calculates some colour space alternative values.

When I include one in the constructor of another object, say:

Color _storeColor;
TestClass(const Color &c) {
    _storeColor = c;
}

or:

Color _storeColor;
TestClass(const Color &c) : _storeColor(c) {}

with:

TestClass myTest(Colors::WhiteSmoke);

the colour data assigned is (nearly always) all 0 as if the constructor for the Color class hasn't been run yet, which I totally get.

So I am looking for ideas on how I can create my library of pre-defined colours in such a way that they will be available to other constructors in the global scope.

Incidentally, things like:

TestClass myTest(Color(245,245,245));

work perfectly, though I'd prefer not to have hundreds (and it is hundreds) or #define macros for the colour library since that would cause lots of unnecessary object duplication and I'd prefer to keep it as always referencing the same global instances whenever a colour is re-used.

like image 503
Majenko Avatar asked Aug 12 '15 12:08

Majenko


People also ask

What is the scope of a constructor method?

The scope of a name declared inside a constructor is the same as the scope of any local name (the fact that it is a constructor makes no difference whatsoever): the scope of the name extends to the end of the block in which the name is declared (and it can have "holes" when the name is hidden by a declaration of an ...

What is a global constructor?

Global constructors are called after initialization of other global variables and before the main() function is called. Global destructors are invoked during the exit run-time support function, similar to functions registered through atexit. Section 8.9. 2.6 discusses the format of the global constructor table.

Can constructor be protected in C++?

Typically, constructors have public accessibility so that code outside the class definition or inheritance hierarchy can create objects of the class. But you can also declare a constructor as protected or private . Constructors may be declared as inline , explicit , friend , or constexpr .

Is default constructor mandatory in C++?

No, the C++ compiler doesn't create a default constructor when we initialize our own, the compiler by default creates a default constructor for every class; But, if we define our own constructor, the compiler doesn't create the default constructor.


2 Answers

The C++ standard does not define the order in which constructors in different translation units get called, as you are aware.

But most C++ implementations typically provide means of specifying the constructor initialization order, which you might be able to use to your advantage.

For example, gcc has the init_priority attribute that you can attach to a constructor and control the constructor's initialization order with respect to other constructors. This would likely be the answer in gcc's case.

Check your compiler's documentation for more information on what compiler-specific features it offers in this area.

For a more portable approach, it might be possible to do something with the fact that PODs get initialized before non-trivial class instances, at namespace scope. It might be possible to leverage this, in order to come up with some approach here, but I'll suggest investigating your compiler's abilities, first. There's nothing wrong with taking advantage of additional features that your compiler offers you.

like image 120
Sam Varshavchik Avatar answered Oct 01 '22 02:10

Sam Varshavchik


What you encountered is sometimes called "static initialization order fiasco".

One of the ways of dealing with it would be using Construct On First Use Idiom, implemented e.g. by changing your colours definitions to getter functions:

const Color & Snow(void) {
    static Color snow(255,250,250);
    return snow;
}

You can read more in this article.


Edit: to avoid excessive amounts of code, you can simply define a helper macro:

#define DEF_COLOR(name, r, g, b) \
const Color & name(void) { \
    static Color name(r,g,b); \
    return name; \
}

DEF_COLOR(Snow,       255,250,250)
DEF_COLOR(GhostWhite, 248,248,255)
// ...
like image 20
Tomasz Sodzawiczny Avatar answered Oct 01 '22 01:10

Tomasz Sodzawiczny