0

This is another variation of an old theme: The initialization order of static objects in different translation units is not defined.

Below is a stripped-down example of my particular scenario. The classes G and F are non-POD types. F depends on G is the sense that to construct an instance of F you need some number of instances of G. (For example, F could be some message an application emits, and instances of G would be components of such messages.)

G.hpp

#ifndef G_HPP
#define G_HPP

struct G
{
    G() {} // ...
};

inline G operator+(G, G) { return G(); }

#endif

Gs.hpp

#ifndef GS_HPP
#define GS_HPP

#include "G.hpp"

extern const G g1;
extern const G g2;
extern const G g3;
extern const G g4;
extern const G g5;
extern const G g6;
extern const G g7;
extern const G g8;
extern const G g9;

#endif

Gs.cpp

#include "Gs.hpp"

const G g1;
const G g2;
const G g3;
const G g4;
const G g5;
const G g6;
const G g7;
const G g8;
const G g9;

F.hpp

#ifndef F_HPP
#define F_HPP

#include "G.hpp"

struct F
{
    F(G) {} // ...
};

#endif

Fs.hpp

#ifndef FS_HPP
#define FS_HPP

#include "F.hpp"

extern const F f1;
extern const F f2;
extern const F f3;

#endif

Fs.cpp

#include "Fs.hpp"
#include "Gs.hpp"

const F f1(g1 + g2 + g3);
const F f2(g4 + g5 + g6);
const F f3(g7 + g8 + g9);

F's constructor takes an argument which is the result of applying operator+ to instances of G. Since the instances of both F and G are global variables, there is not guarantee that the instances of G have been initialized when the constructor of F needs them.

The particularity here is that there are many Gs and Fs all over the place, and I would like to keep the syntax as much as possibly close to the code posted above, while still enforcing the construction of a G whenever an F needs it.

Matthew Murdoch
  • 30,874
  • 30
  • 96
  • 127
cj.
  • 250
  • 1
  • 3
  • 13
  • Have you noticed your +operator does not actually use arguments? ;-) – Roman Nikitchenko Sep 29 '10 at 22:41
  • Yeah, I just put it in to make the example code compile. But the idea was that operator+ combines its arguments (e.g. parts of an application message) so we can create an F (e.g. the application message) from that combination. – cj. Sep 29 '10 at 22:57
  • Yes, but funny thing is your code behavior is guaranteed ;-))). – Roman Nikitchenko Sep 29 '10 at 23:06
  • I took a stab at a reusable solution to this problem: https://github.com/wkaras/c-plus-plus-misc/blob/master/ORD_INIT/ord_init.h . – WaltK Oct 21 '17 at 02:10

3 Answers3

1

From http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.15 .

Change your global objects into functions which construct the object on first use.

// Gs.hpp
const G & g1();

// Gs.cpp
const G & g1() {
  static const G* g1_ptr = new G();
  return *g1_ptr;
}

// Fs.cpp
const F & f1() {
  static const F* f1_ptr = new F(g1() + g2() + g3());
  return *f1_ptr;
}

Or if you really can't stand adding the extra ()s, use some #defines to hide them:

// Gs.hpp
const G & get_g1();
#define g1 (get_g1())
// Definition of get_g1() like g1() from prev. example
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • Would be good the get rid of the `gN` function definition boilerplate, as well. The fs don't need to be functions as nothing else static depends on them. – cj. Sep 29 '10 at 23:04
  • Yes, the fNs don't need this change to make this example safe. But I prefer to just protect all global variables in this way so I don't run into another initialization problem all over again when I add or change some other code. – aschepler Sep 29 '10 at 23:16
  • Instead of having N identical `get_gN` functons, you could use a template: `template G& get_g() { static G g; return g; }`. You could use these with either a plain `get_g<1>()` or using `#define G1 get_g<1>()` – Bart van Ingen Schenau Sep 30 '10 at 15:30
0

Keep the extern declarations in the headers. Put all the fN and gN definitions into a single cpp file in the appropriate order.

wilx
  • 17,697
  • 6
  • 59
  • 114
  • Unfortunately, I have many fs and gs in various libraries, so I cannot put them in a single cpp file. – cj. Sep 29 '10 at 23:01
0

Maybe a trick similar to the one used to initialize cin and friends' filebuffers would work for you? (Read <iostream> carefully.)

zwol
  • 135,547
  • 38
  • 252
  • 361