Manual construction of static/global variables (VC2012)

Started by
13 comments, last by King Mir 10 years, 5 months ago
I need to construct some static/global variables (dissing pointers here) before they normally would, unfortunately "init_seg" is unusable for this purpose (*massive-palmface*) due of the grossly ridiculous translation unit restrictions. I can not think of any way to prevent the default construction calls being generated ... except something like this as a generic "ManuallyConstructed" wrapper:
template<class C> class MC {
public:
    // do-nothing default constructor
    MC() {}
    // manual construction: cannot use variadic templates as my version of VC2012 does not support them and i have not confirmed whether the update has XP target support - minor inconvenience anyway.
    void construct() { new(_object) C(); }
    template<typename T1> void construct(T1 par1) { new(_object) C(par1); }
    template<typename T1, typename T2> void construct(T1 par1, T2 par2) { new(_object) C(par1, par2); }
    template<typename T1, typename T2, typename T3> void construct(T1 par1, T2 par2, T3 par3) { new(_object) C(par1, par2, par3); }
    template<typename T1, typename T2, typename T3, typename T4> void construct(T1 par1, T2 par2, T3 par3, T4 par4) { new(_object) C(par1, par2, par3, par4); }
    // manual destruction
    void destruct() { ( operator->() )->~C(); }
    // would like to overload "." ... but that got stuck in the fucking committee
    FORCEINLINE C *operator->() const { return (C*)_object; }
private:
    // space for the actual object. (with arbitrary/terrible alignment as thous do not work: "alignas(C)", "__declspec(align(std::alignment_of<C>::value))" )
    __declspec(align(16)) byte _object[sizeof(C)];
    assertS(std::alignment_of<C>::value <= 16);
    // pre-emptively disable nonsense, but might implement later - YAGNI. (does not work: "=delete")
    MC &operator=(MC const &);
    MC &operator=(MC const &&);
    MC(MC const &);
    MC(MC const &&);
};
It works - as far as i can see. That is,
Test mcTest0(44);
MC<Test> mcTest1, mcTest2;

void testInit() { // called before global variables get constructed
    mcTest1.construct();
}

void testExit() { // called after global variables get destructed
    mcTest1.destruct();
}

void testMain() { // called in WinMain
    mcTest2.construct(77);
}
produces the expected order of events:
* constructed: mcTest1, mcTest0, mcTest2
* destructed: mcTest0, mcTest1

Grievances:
* Need to use "->" instead of "." to get to the actual object. Argh, is there any way around it? Perhaps some completely different approach?
* Cannot specify correct alignment. Did i miss an option / workaround for VC2012?
* Darn wrapper again - there is always a chance i missed something. Is there anything that could bite me in future? Any obvious mistakes?
edit:
* It gets darn "funny" to use when the wrapped object also has an overloaded "->". Can not think of anything to alleviate that atm. Ie. the "." vs "->" is even more annoying that i initially thought.
Advertisement

I'm confused... why not just use a Meyers Singleton?

Why not just follow the standard advice and not use globals?

Seems to me that you're going out of your way to pervert the language into doing something it really really doesn't want you to do, something the language designers tried to make it hard for you to do, so you can do something that has been proved bad practice for decades. Using globals was frowned on in the 1950s for goodness sake.. Oh, sure I know, you're different, you really have discovered the answer and this damnable tool is to blame for your not being able to achieve programming Nirvana.

Or, maybe, if you find you need to fight against the language, it really just means you need to rethink your design. That's always been my experience anyways.

Stephen M. Webb
Professional Free Software Developer

Surely you mean constructed after they normally would, ie. two stage construction.

Why is destruct a function? that kind of undermines using destructors and can lead to problems if there's a code path that doesn't call it, or calls it at the wrong time.

Perhaps instead of overloading -> and *, you could just implement a .get() function that doesn't make the thing look like a pointer style wrapper.

But this problem is IMO rare enough that It's better to insert a construct function for those classes that need it instead of using a template.

Better just make those other globals, that depend on your global you want to construct first, nonglobal and the problem vanishes.rolleyes.gif

(Or maybe make the depended on global a member of or something constructed by the dependant globals.)

I'm confused... why not just use a Meyers Singleton?

Confused is good, confused i can work with.

One of the manually constructed objects is used, under the hood, everywhere with performance implications: all the interactions with the manually constructed object are in the overhead category (currently: 4 basic instructions, no branching, all memory interactions expected to be in L1). Overhead is time-comparable to useful work - ie. the overhead matters. So, Meyers Singleton would be disastrously bad in comparison of just using another layer of pointer indirection which i dissed also.

Why not just follow the standard advice and not use globals?

You are misunderstanding the topic. I'm of course using private static member variables of an instanced class - which do not differ from globals as far as the standard construction mechanic is concerned. The very mechanic this thread is dealing with - hence "static/global" as it is not relevant to the discussion which-ever the object is.

Surely you mean constructed after they normally would, ie. two stage construction.

No. Construction before - as the constructed object is used before and while standard initialization runs.

Why is destruct a function? that kind of undermines using destructors and can lead to problems if there's a code path that doesn't call it, or calls it at the wrong time.

Agreed. However - how do i do that? I can not do it in MC destructor as that would be guaranteed to be called in the wrong time and crash. Hm... adding it manually to the destruction chain in the constructor would work. IIRC. "atexit" goes to the same list as destructors in VC, but do i have any backing from standard?

Perhaps instead of overloading -> and *, you could just implement a .get() function that doesn't make the thing look like a pointer style wrapper.

I am inclined to agree. My mindset was to use ".", but since that is not possible yet - just having a ".get()" is way better in every respect.

But this problem is IMO rare enough that It's better to insert a construct function for those classes that need it instead of using a template.

That is actually exactly what i used so far - it turned out to be a horrible approach that i really-really need to get rid of now.

Better just make those other globals, that depend on your global you want to construct first, nonglobal and the problem vanishes.rolleyes.gif

Unfortunately, that is not possible. Also, as the underlying problem is bottleneck-performance then this would actually be a way to make the problem worse ;).

No. Construction before - as the constructed object is used before and while standard initialization runs.

So make all values being zero be a valid state, and use two stage construction.

Why is destruct a function? that kind of undermines using destructors and can lead to problems if there's a code path that doesn't call it, or calls it at the wrong time.

Agreed. However - how do i do that? I can not do it in MC destructor as that would be guaranteed to be called in the wrong time and crash. Hm... adding it manually to the destruction chain in the constructor would work. IIRC. "atexit" goes to the same list as destructors in VC, but do i have any backing from standard?

I would expect it to be called in the MC destructor, which, like any global, goes out of scope after main, in reverse order of initialization. Why is that the wrong time?

But this problem is IMO rare enough that It's better to insert a construct function for those classes that need it instead of using a template.

That is actually exactly what i used so far - it turned out to be a horrible approach that i really-really need to get rid of now.

But this is the exact same thing wrapped in a template. What problem are you trying to solve by using a template?

So make all values being zero be a valid state, and use two stage construction.

Cannot do that. It would, in this case, be comparable to the Meyers Singleton proposed earlier. Object must be in a fully constructed state before first usage (used before and while other globals get constructed).

Agreed. However - how do i do that? I can not do it in MC destructor as that would be guaranteed to be called in the wrong time and crash. Hm... adding it manually to the destruction chain in the constructor would work. IIRC. "atexit" goes to the same list as destructors in VC, but do i have any backing from standard?

I would expect it to be called in the MC destructor, which, like any global, goes out of scope after main, in reverse order of initialization. Why is that the wrong time?

MC constructor does nothing - hence its destruction time is not related to the wrapped object in any way as MC did not construct it in the first place ("construct" function did).

That is the whole point of the wrapper: disable standard construction behavior for out-of-order construction and still maintain direct addressability.

That is actually exactly what i used so far - it turned out to be a horrible approach that i really-really need to get rid of now.

But this is the exact same thing wrapped in a template.

No, it is not. It forces the class to have unnatural/unrelated properties (ex: requiring an empty constructor which does not necessarily make any sense for the class's point of view) vs. the MC approach which does not require a "special-kind-of" class to work (one ex: i need to use "circular buffer with guard page" class for its normal lifetime instances and for its manually constructed instances).

So make all values being zero be a valid state, and use two stage construction.

Cannot do that. It would, in this case, be comparable to the Meyers Singleton proposed earlier. Object must be in a fully constructed state before first usage (used before and while other globals get constructed).

There's no way to control the order that globals are constructed in except by putting them in the same translation unit. You can only delay the logical construction of some globals.

Agreed. However - how do i do that? I can not do it in MC destructor as that would be guaranteed to be called in the wrong time and crash. Hm... adding it manually to the destruction chain in the constructor would work. IIRC. "atexit" goes to the same list as destructors in VC, but do i have any backing from standard?

I would expect it to be called in the MC destructor, which, like any global, goes out of scope after main, in reverse order of initialization. Why is that the wrong time?

MC constructor does nothing - hence its destruction time is not related to the wrapped object in any way as MC did not construct it in the first place ("construct" function did).

That only means you need to call the sub-object destructor explicitly in MC's destructor.

That is the whole point of the wrapper: disable standard construction behavior for out-of-order construction and still maintain direct addressability.

That is actually exactly what i used so far - it turned out to be a horrible approach that i really-really need to get rid of now.

But this is the exact same thing wrapped in a template.

No, it is not. It forces the class to have unnatural/unrelated properties (ex: requiring an empty constructor which does not necessarily make any sense for the class's point of view) vs. the MC approach which does not require a "special-kind-of" class to work (one ex: i need to use "circular buffer with guard page" class for its normal lifetime instances and for its manually constructed instances).

So you need the class to be addressable before it is constructed? You might be able to just use extern declarations to make the variable token available before it is constructed.

*edit: nevermind this post, everything I would have to say has been pretty much covered by King Mir...

This topic is closed to new replies.

Advertisement