Reputation: 1840
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.
Upvotes: 5
Views: 394
Reputation: 347
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)
// ...
Upvotes: 3
Reputation: 11963
If you can use C++11
, you may also try to arrange your preprocessing such that it can be computed at compile time and make the color definitions a constexpr
:
class Color {
public:
int m_r, m_g, m_b;
constexpr Color(int r, int g, int b) : m_r(r), m_g(g), m_b(b) {}
constexpr Color(Color const& o) : m_r(o.m_r), m_g(o.m_g), m_b(o.m_b) {}
};
constexpr const Color RED = Color(255,0,0);
constexpr const Color BLUE = Color(0,255,0);
constexpr const Color GREEN = Color(0,0,255);
This should guarrante that the colors are initialized before other constructors are called (constant initialization takes place before dynamic initialization) . It has the additional benefit that the compile may perform your preprocessing at compile time, so it might even be a little more efficient (however, this also means it won't work if your preprocessing depends on values only available during run-time).
Upvotes: 1
Reputation: 118340
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.
Upvotes: 5