init_seg and warning C4073 from library code?

2019-07-10 15:56发布

We have a C++ library. It has four static objects that are sensitive to initialization order (two of them are strings from the standard library).

We are using init_seg(lib) to control the order of initialization of C++ static objects in the library. The source file that is using it is compiled and used in either a dynamic link library or static library. According to the documentation for init_seg:

... It is particularly important to use the init_seg pragma in dynamic-link libraries (DLLs) or libraries requiring initialization. (emphasis mine)

The Visual Studio solution is organized into four projects. One is the static library, or is the dynamic library, one is the test driver for the static library, and one is the test driver for the dynamic library.

Under Visual Studio, compiling the source file with the init_seg results in warning C4073, with the text initializers put in library initialization area. According to MSDN:

Only third-party library developers should use the library initialization area, which is specified by #pragma init_seg. The following sample generates C4073...

The code using the init_seg is used in the libraries only, and not used with the test drivers. I've verified the static library and dynamic library project settings, and they are clearly calling out library artifacts.

Why am I receiving the C4073 warning?

1条回答
来,给爷笑一个
2楼-- · 2019-07-10 16:51

It's just warning you, not even a wag of the finger, it's more like "are you sure?" When you use a 3rd party library that uses this feature it warned its developer too - unless he turned it off with #pragma warning. You could do the same. Or you could use segment user not segment lib: your strings will still be constructed before your application code runs. Segment lib is really intended for things like ... oh, Qt or MFC or a framework like that that needs to be initialized before any application code runs, including your early initialization stuff in user.

Here's some more information:

Suppose you have a library for your own app. And it has some stuff that needs to be initialized before any of its code is run, because certain classes exposed by this library are intended to be (or, are allowed to be) statically allocated in your application code, and those classes do complicated stuff in their constructors that needs, say, some large bollix of precomputed (but not static) data. So for that precomputed stuff you precompute it in the constructor of a class (a different class) and you statically allocate an instance of that class, so that the initialization of that instance calls its constructor which precomputes all that stuff, and that static instance you mark with pragma init_seg(user). Now, before any of your application code runs - including any constructors of static instances of this library's class in your code - the library's init_seg(user) code will all run, and so, by the time your static instances of this library's classes get constructed the data they need will be there.

Now consider stuff that really has got to be there early, of which static instances exist that you can call. E.g., std::cout. You can call methods on std::cout in constructors of your own classes that you have static instances of. Obviously, the object std::cout needs to be initialized before any of your code runs. And some of your code might run in stuff you've marked init_seg(user). So Microsoft places all such code - that is, the constructors for std::cout etc. - in init_seg(compiler). That stuff will run before anything else.

So what is init_seg(lib) for? Well, suppose you're a framework like MFC. You expose stuff like the Application object that the user will (is likely to, is expected to) create a static instance of. Some of the code in Application which will be run at static initialization time depends on other stuff in MFC that needs to be initialized. So obviously it needs to be init_segged, but where? compiler is for the compiler and runtimes alone, and your framework stuff (MFC) might be used in the init_seg(user) that the user is allowed to use ... so you get an intermediate level between compiler and user - that's lib.

Now it is fairly rare to need anything like that because most C++ libraries used by programs are not themselves used by multiple libraries and thus don't need to make sure they're initialized before all other "ordinary" libraries. MFC does, because you might have bought 3rd party MFC control or other libraries from one or more vendors, and those depend on MFC, and those might use MFC things in their constructors, and you might use objects from those libraries statically, so MFC needs to be initialized before those other libraries.

But in most cases, your library isn't going to be a dependency of other C++ libraries that people are using. Thus there isn't any kind of dependency chain where your library needs to be initialized before other libraries. Your libraries might need to be initialized before your user code, yes, but there isn't any order in which they need to be initialized. So init_seg(user) is fine for all of them.

And, Microsoft (and generally, most C++ experts) will tell you: If there is some kind of order dependency in which your separate libraries need to be initialized at the time of static initialization of your app then you're doing it wrong. Seriously. There's a design problem there.

So, to respond to a comment: It isn't a compiler bug. It's a user warning. Fairly benign: Nothing's going to go wrong if you ignore it (unlike, say ignoring a warning about casting from long to int). But if you're using init_seg(lib) you might not really be understanding what that compiler feature is all about, so they'd like you to think about it. And after you've thought about it, if you still want to do it, go ahead and turn the warning off.

查看更多
登录 后发表回答