2

I'm seeing something weird happening with Visual Studio 2015 Community. Code that worked perfectly in VS2012 crashes at startup when ported to VS2015, before main is invoked: the classic symptoms of some static initialization mess. I have a few static variables, but used properly, with the standard "Construct On First Use" pattern, e.g.:

const int& i()
{
  static int *v = new int(1);
  return *v;
}

The code above causes an assert from the runtime while initializing the program (see image https://i.stack.imgur.com/nsQGS.png). Pressing retry simply quits the program: no call stack, no information whatsoever.

The code below however works flawlessly:

const int& i()
{
  static int *v = nullptr;
  if (v == nullptr)
    v = new int(1);
  return *v;
}

It seems to me that VS2015 is doing what looks like some (illegal) optimization (even in a debug build), by executing the static variable initialization code during program initialization, and not the first time that the function is called, as the C++ standard requires. I tried several variations of the code above: class methods, free functions, different objects (std::vector, cv::Mat), always with the same result: the static pointer has to be initialized to null or the program doesn't start.

So... am I missing something or is VS2015 actually messing up badly?

UPDATE:

I spent some time trying to find the minimal setup that shows the problem, and this is what I got:

  1. create a project using the "CLR empty project" template.
  2. add a cpp file, containing just the offending function and main(). Main can be empty, it doesn't matter: the bug occurs BEFORE it is even reached.
  3. add 'main' as entry point (or you get a link error).

The x86 version works, but the x64 doesn't.

For comparison, a project with the identical code but created as "Win32 console application" doesn't have the problem, even after adding the /CLR option. I can see differences in the vcxproj files, but none justifies the error, although one or more of them clearly IS the cause.

Is there a way to upload a zip with the project?

thebucc
  • 207
  • 1
  • 10
  • why do you need to initialize object on heap? – Slava Aug 18 '15 at 17:39
  • Is it crashing having a simple `int main() { int v = i(); }` ? –  Aug 18 '15 at 17:43
  • Are you running in Release mode or Debug? With or without the optimization? – Alex Lop. Aug 18 '15 at 17:49
  • 1
    Works fine here. Test case `int main() { int x = i(); return x; }` with the first version of `i()`. – Bo Persson Aug 18 '15 at 18:27
  • Added an `int y = i();` outside main. No difference. – Bo Persson Aug 18 '15 at 18:30
  • @AlexLop.: Debug build, without optimizations. Release build (with optimizations) is the same, except it crashes straight away without the error message, which comes from the debug heap. – thebucc Aug 18 '15 at 19:33
  • @Slava: actually initially in some cases I had objects, as you suggest, and it works for a simple int, but for example this also crashes: const std::vector& i2() { static std::vector v; return v; } The objects I'm actually using are cv::Mat and other classes from OpenCV – thebucc Aug 18 '15 at 19:48
  • Does this happen with a minimal hello world program in which you have only your example code? Maybe the problem is that the heap is already screwed up when this code is executed (perhaps because of some other static initializers executing before this one). The fact that it ran successfully with VS2012 doesn't mean that your code isn't buggy. This is why I like multiplatform development with many compilers, sometimes a bug doesn't lead to crash/problem on some platforms. – pasztorpisti Aug 18 '15 at 21:16
  • @BoPersson: see if my last update answers your doubts. – thebucc Aug 19 '15 at 05:10
  • Side note: there is honestly no point in returning `const int&` here. You might as well return `int`. – barak manos Aug 19 '15 at 05:42
  • @thebucc - I have never used a CLR-project, so of course I tried the "Win32 console application", which works for you too. – Bo Persson Aug 19 '15 at 07:04
  • Can you show the generated (assembler) code in the first example? – skyking Aug 19 '15 at 07:31
  • "add 'main' as entry point (or you get a link error)" - that doesn't sound right. The correct entry point for a console application in MSVC is something like [`mainCRTStartup`](https://msdn.microsoft.com/en-us/library/f9t8842e.aspx) (I'm not sure about the exact name for CLR code, as I've never used that). The function specified as entry point needs to initialize stuff in the CRT, among other things, before calling `main`. Anyway, I tried the first two steps and everything worked fine, both `x86` and `x64`, without the need to specify the entry point, so there's something else going on there. – bogdan Aug 19 '15 at 09:25
  • @BoPersson: yes, the problem is clearly only with CLR, and even then only with some combinations of settings. – thebucc Aug 19 '15 at 14:21
  • I've reproduced your problem by setting `main` as the entry point in the linker settings, so that's probably the cause, as explained above. – bogdan Aug 19 '15 at 14:47
  • @bogdan: which template did you use? "CLR empty project" doesn't automatically add an entry point, nor does it create any cpp file, you have to do all the work yourself. Can you explain your steps? – thebucc Aug 19 '15 at 14:59
  • @barakmanos. Of course, What I've written is the simplest code that causes the problem, so it's clear that the problem is not with the specific class and libraries I'm using. – thebucc Aug 19 '15 at 15:48
  • I followed exactly steps 1 and 2 in your description. Indeed, the template doesn't set either `Entry Point` or `SubSystem`, which means `/subsystem:console` will be selected if you define `int main()`, which will select the default correct entry point. At least that's how it's supposed to work - it's specified in the docs I linked above. – bogdan Aug 19 '15 at 20:42

1 Answers1

1

Well, @bogdan got it right. My project had a mix of settings that was neither /SUBSYSTEM:CONSOLE nor /SUBSYSTEM:WINDOWS. Once I fixed that, everything started to work as expected. My test projects had the same problem, I blame Microsoft for not having a clear "CLR windows app" C++ template any more in VS2015 (they want to push you to use C# for that, which makes sense most of the times, but not always).

I found this page particularly useful in explaining all the different settings that have to be consistent (it's not just /SUBSYSTEM...).

I wish I could mark @bogdan's comment as answer, but I can't see anything to do that.

Thanks Bogdan!

thebucc
  • 207
  • 1
  • 10