28

Suppose I have a class whose only purpose is the side-effects caused during construction of its objects (e.g., registering a class with a factory):

class SideEffectCauser {
public:
  SideEffectCauser() { /* code causing side-effects */ }
};

Also suppose I'd like to have an object create such side-effects once for each of several translation units. For each such translation unit, I'd like to be able to just put an a SideEffectCauser object at namespace scope in the .cpp file, e.g.,

SideEffectCauser dummyGlobal;

but 3.6.2/3 of the C++03 standard suggests that this object need not be constructed at all unless an object or function in the .cpp file is used, and articles such as this and online discussions such as this suggest that such objects are sometimes not initialized.

On the other hand, Is there a way to instantiate objects from a string holding their class name? has a solution that is claimed to work, and I note that it's based on using an object of a type like SideEffectCauser as a static data member, not as a global, e.g.,

class Holder {
  static SideEffectHolder dummyInClass;
};

SideEffectHolder Holder::dummyInClass;

Both dummyGlobal and dummyInClass are non-local statics, but a closer look at 3.6.2/3 of the C++03 standard shows that that passage applies only to objects at namespace scope. I can't actually find anything in the C++03 standard that says when non-local statics at class scope are dynamically initialized, though 9.4.2/7 suggests that the same rules apply to them as to non-local statics at namespace scope.

Question 1: In C++03, is there any reason to believe that dummyInClass is any more likely to be initialized than dummyGlobal? Or may both go uninitialized if no functions or objects in the same translation unit are used?

Question 2: Does anything change in C++11? The wording in 3.6.2 and 9.4.2 is not the same as the C++03 versions, but, from what I can tell, there is no behavioral difference specified for the scenarios I describe above.

Question 3: Is there a reliable way to use objects of a class like SideEffectHolder outside a function body to force side-effects to take place?

Community
  • 1
  • 1
KnowItAllWannabe
  • 12,972
  • 8
  • 50
  • 91
  • I think we discussed this some time ago, and neither C++03 nor C++11 strictly require that the statics are constructed if no function in the TU is called, but no compiler would exploit that because it would break everyone's code... – Kerrek SB Dec 05 '12 at 02:07
  • 5
    To answer your first question: In C++03, the [Schwarz counter](http://en.wikibooks.org/wiki/More_C++_Idioms/Nifty_Counter) will guarantee that static classes, `dummyInClass` and `dummyGlobal` are initialized, whether or not their objects are called. This approach removes any uncertainty relating to initialization or its absence. – damienh Dec 05 '12 at 02:15
  • @KerrekSB: Unfortunately, my question is prompted by the experience of a colleague who finds that with static linking, everything works, but with dynamic linking, it does not. One would like to think that when a TU is brought in via dynamic linking, the non-local statics in that TU would be dynamically initialized, but this does not seem to be happening, because nothing in that TU is used (and won't be until the non-local statics have their side-effects). – KnowItAllWannabe Dec 05 '12 at 02:16
  • 3
    +1 for having a very well stated question, but the purpose of doing such a thing is dubious. This would imply that mearly linking to the object file would cause a change in behavior of the application, even if the application would link without the object file. – Vaughn Cato Dec 05 '12 at 02:16
  • @damienh: The Schwarz counter is simply a way of implementing non-local statics. In and of itself, it does not guarantee that those non-local statics are initialized if nothing else in the translation unit is used. – KnowItAllWannabe Dec 05 '12 at 02:50
  • When you say dummyGlobal is not initialized, how do you know? If it is another global object trying to use it you are hitting the global object ctor order issue. Also be careful if dummyGlobal is in multiple shared objects (.so files) on UNIX because they can share the same memory. – brian beuning Dec 05 '12 at 02:53
  • @brianbeuning: We know that `dummyGlobal` is not being initialized, because its side effects (adding data to a collection) are not taking place. – KnowItAllWannabe Dec 05 '12 at 02:57
  • [This](http://stackoverflow.com/q/6420985/500104) might be of interest / loosely related. – Xeo Dec 05 '12 at 03:16
  • @VaughnCato: Sometimes I do wish it to happen. And I believe compared with a static object being constructed by an unrelated function call simply because they're in the same translation unit, the behavior based on linking is much more stable. – hpsMouse Dec 05 '12 at 05:38
  • @hpsMouse: In your example of registering classes with a factory, I would suggest registering them all explicitly. – Vaughn Cato Dec 05 '12 at 05:47
  • @VaughnCato: That's what I do in my programs. And KnowItAllWannabe asked the question, not me. :) – hpsMouse Dec 05 '12 at 08:41
  • I wonder if adding a nameless namespace function (they have external linkage), that just returns (void*)&dummyGlobal would work. When dynamic linking, that object should be available, since the function could return a reference to it.. – Jan Dec 05 '12 at 18:02
  • 2
    @Jan : Unlike in C++03, in C++11 functions in unnamed namespaces have internal linkage unless otherwise specified. – ildjarn Dec 05 '12 at 20:14
  • @ildjarn Oh thanks, I'm still living in the past :) So specify external linkage, and it should work? – Jan Dec 06 '12 at 07:52
  • Just in case, if you are using shared libraries ("shared object", "dynamic link libraries") your static members may be duplicated ... – umlcat Dec 19 '12 at 00:05

2 Answers2

1

I think the only reliable solution is to design this for specific compiler(s) and runtime. No standard covers the initialization of globals in a shared library which I think is the most intricate case, as this is much dependent on the loader and thus OS dependent.

Q1: No Q2: Not in any practical sense Q3: Not in a standard way

Jojje
  • 381
  • 3
  • 13
0

I'm using something similar with g++ / C++11 under Linux and get my factories registered as expected. I'm not sure why you wouldn't get the functions called. If what you describes is to be implemented it will mean that every single function in that unit has to call the initialization function. I'm not too sure how that could be done. My factories are also inside namespaces, although it is named namespaces. But I don't see why it wouldn't be called.

namespace snap {
namespace plugin_name {
class plugin_name_factory {
public:
  plugin_name_factory() { plugin_register(this, name); }
...
} g_plugin_name_factory;
}
}

Note that the static keyword should not be used anymore in C++ anyway. It is often slower to have a static definition than a global.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156