15

I use C++ in Visual Studio 2015 sp3. By

#pragma init_seg(compiler)

, I initialize some static variables first(to memory management). https://msdn.microsoft.com/en-us/library/7977wcck.aspx

But, there is

#pragma init_seg(compiler)

in wcerr.cpp(Microsoft Visual Studio 14.0\VC\crt\src\stl\wcerr.cpp), so these objects are initialized before my objects.

Can I compel my object to be initialized first before wcerr.cpp objects by any compile / link options?

Marco A.
  • 43,032
  • 26
  • 132
  • 246
P-P
  • 1,670
  • 5
  • 18
  • 35
  • why does the order matter? – Cheers and hth. - Alf Dec 09 '16 at 08:08
  • @Cheersandhth.-Alf Because my object initializes the custom memory system, if some other objects call new before my object, it makes error. :( – P-P Dec 09 '16 at 08:42
  • 4
    So the actual question is - [how to override a memory allocator in MSVC](http://stackoverflow.com/questions/12815867/overriding-memory-allocator-in-msvc) – rustyx Dec 09 '16 at 10:39

3 Answers3

14

One of solutions is to try wrap your static variables to static functions:

static type& My_static_obj() {
    static type my_static_obj_;
    return my_static_obj_;
}

It looks like a simple type of Singleton and calls Construct On First Use Idiom. Due to standard (C++11 and above) it guaranteed to be initialized once (and even atomically!), and inside its c-tor such an object can access to other "static" variables, so, if there aren't circular dependencies between variables, the order of initialization will be strictly defined.

For additional information see this question and other descriptions of this Construct On First Use Idiom.

Community
  • 1
  • 1
Trollliar
  • 826
  • 5
  • 14
  • 'Construct On First Use Idiom' is good information. Thanks. But I have to construct the object before werr in Microsoft Visual Studio 14.0\VC\crt\src\stl\wcerr.cpp. Can I use this idiom for wcerr? – P-P Dec 21 '16 at 02:40
  • [Standard](http://en.cppreference.com/w/cpp/io/cerr) says it (_possible_) can be achieved by including `` _after_ your object's initialization (2nd paragraph, it causes `std::ios_base::Init` [initialization](http://en.cppreference.com/w/cpp/io/ios_base/Init)), maybe you will have to do forward declarations for `wcerr`. Or you can explicitly extend `std::ios_base::Init` c-tor for your purposes, but it's a dirty hack. Try and let us know. One more time: in general, as was mentioned, order isn't defined, and seems like VS and Win will try initialize their utility before any user's code. – Trollliar Dec 21 '16 at 12:16
  • 1
    Nit picking for ultimate correctness: each "type" in the answer's code should be "type const" unless the my_static_obj_ instance is intended to be modifiable. If it is intended to be modifiable, then there should be both const and lacking-const overloaded My_static_obj functions, so that the compiler can choose the appropriate read-only versus modifiable at its point of usage. Likewise for rvalue-reference overloading. (And if my_static_obj_ is positioned at a fixed address as DMA to an IC's registers such as in device drivers, then likewise for volatile overloading.) – Andreas ZUERCHER Mar 27 '18 at 17:46
3

Probably the nifty counter idiom can help you somehow in this case:

Ensure a non-local static object is initialized before its first use and destroyed only after last use of the object.

Its motivation is pretty clear:

When static objects use other static objects, the initialization problem becomes more complex. A static object must be initialized before its use if it has non-trivial initialization. Initialization order of static objects across compilation units is not well-defined. Multiple static objects, spread across multiple compilation units, might be using a single static object. Therefore, it must be initialized before use. One example is std::cout, which is typically used by a number of other static objects.

It's worth it to copy and paste directly the example from the above linked page:

Stream.h

#ifndef STREAM_H
#define STREAM_H

struct Stream {
  Stream ();
  ~Stream ();
};
extern Stream& stream; // global stream object

static struct StreamInitializer {
  StreamInitializer ();
  ~StreamInitializer ();
} streamInitializer; // static initializer for every translation unit

#endif // STREAM_H

Stream.cpp

#include "Stream.h"

#include <new>         // placement new
#include <type_traits> // aligned_storage

static int nifty_counter; // zero initialized at load time
static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type
  stream_buf; // memory for the stream object
Stream& stream = reinterpret_cast<Stream&> (stream_buf);

Stream::Stream ()
{
  // initialize things
}
Stream::~Stream ()
{
  // clean-up
} 

StreamInitializer::StreamInitializer ()
{
  if (nifty_counter++ == 0) new (&stream) Stream (); // placement new
}
StreamInitializer::~StreamInitializer ()
{
  if (--nifty_counter == 0) (&stream)->~Stream ();
}

The header file of the Stream class must be included before any member function can be called on the Stream object. An instance of the StreamInitializer class is included in each compilation unit. Any use of the Stream object follows the inclusion of the header, which ensures that the constructor of the initializer object is called before the Stream object is used.

See the link above for further details.

skypjack
  • 49,335
  • 19
  • 95
  • 187
1

EDIT: I think the solution you need is to use another STL implementation, if you need to use MSVC 2015 and also avoid reimplementing your current memory management hack (sorry to call it that but that's what it sounds like, but then again I've used a few hacks of my own to work with MSVC).

I have never used an STL implementation that was not included with the development environment, but it is possible. It should be a simple matter of setting your include and library paths to the alternative STL implementation. You might start with trying STLPort

Otherwise, my original advice regarding your question, before the last edit:

Static initialization order within a compilation unit (cpp file) is the order in which the declarations are made. Static initilization order between compilation units is undefined. Do not use static initialization to solve this problem.

It looks like RustyX is pointing you in the right direction with his comment linking to Overriding memory allocator in MSVC++.

EDIT: It looks like the answer you are looking for might be here: How to properly replace global new & delete operators. Note that both answers are relevant, you will want to replace new, delete, new[] and delete[]. Since this is applied during linking, apparently Windows will not use these for DLLs you load (see comments on the first answer).

Community
  • 1
  • 1
John Thoits
  • 355
  • 1
  • 12
  • Because the memory management code is not just mine, but in frameworks. Before MSVC 2015, it always constructed before wcerr. Even in MSVC 2015, in Debug build, my static object is constructed before wcerr. Only in Release build, object construction order is changed. – P-P Dec 21 '16 at 02:49
  • Sounds like a forward compatibility issue then. You got lucky in previous MSVC versions, I would say don't use MSVC 2015. You were using a special compiler directive that shouldn't have been relied on for the reasons I stated about static initialization order. Compilers (and libraries) change. You're only option for using MSVC 2015 I think is to not use Microsoft's STL, or possibly modify it and build your own (but I don't think it's licensed for that purpose). Boost has a reputable STL implementation for example. – John Thoits Dec 21 '16 at 19:04