3

I've experienced a weird behavior on VS2010 part.

I've added this simple code to two .cpp files, and placed a breakpoint on the specified line of code

namespace {
   class TestClass
   {
   public:
      TestClass()
      {
         printf("");     // ### BREAKPOINT_HERE
      }
   };
}
TestClass a;

The weird thing is that once the program is compiled and run, breakpoint in one of the files is correct, and in the other one gets automatically disabled with a warning: 'The breakpoint will not currently be hit. No symbols have been loaded for this document.'

Both .cpp files were created in the same fashion and have the very same properites. The project they're in has a significant amount of files, but the file I've problem with wasn't added most recently - there are newer files that don't exhibit the problem.

Can anyone tell me what can be the problem?

Cheers, Paksas

Suma
  • 33,181
  • 16
  • 123
  • 191
Piotr Trochim
  • 693
  • 5
  • 15
  • 1
    is it possible that you try to register the same type twice? – Yakov Galka Dec 29 '10 at 18:15
  • Show the code from both files, please. – Suma Dec 29 '10 at 18:23
  • Try rebuilding the solution -- that error indicates that your debugging symbols aren't valid for the current file. A rebuild should regenerate the debugging symbols. – Billy ONeal Dec 29 '10 at 18:23
  • 5
    You do realize that *entirely* changing the question 21 minutes after it's been posted make the previously provided answers lose any kind of sense ? – icecrime Dec 29 '10 at 18:26
  • After the edit, this looks like a *much* different issue. In particular, are you sure that the second file is actually included in the build? – Dan Breslau Dec 29 '10 at 18:28
  • Yes - sorry - but I'm focused on getting an answer fast, and my previous description was simply irrelevant - I found an underlying problem and would like to make the discussion about it. On the bright side - I didn't expect such quick responses - this is my first post here and I'm truly amazed :) – Piotr Trochim Dec 29 '10 at 18:30
  • > After the edit, this looks like a much different issue. In particular, are you sure that the second file is actually included in the build? -- YES – Piotr Trochim Dec 29 '10 at 18:30
  • Your answer indicates there are some statically linked libraries involved. Are you aware this makes is completely a different issue? – Suma Dec 29 '10 at 18:34
  • What different issue are you reffering to? – Piotr Trochim Dec 29 '10 at 18:39
  • While all static objects from object files participating in a link are used, this is not true for objects included in (statically linked) libs. Therefore if some of your sources are part of libs, it makes a significant difference, as the current top two answers explain. – Suma Dec 29 '10 at 18:42

6 Answers6

1

I have run into a similar problem before, although then I had defined the registering code in a separate LIB project. It turns out that the linker optimized away my objects! My solution was to reference these "registration objects" in a function. I then called the function from my application. Maybe something like that is happening here? Try turning off whole program optimization and see what happens. You might have to do something to keep the optimizer from treating this objects as dead code. (I wish Visual C++ had attributes for marking code as non-dead...)

Update: Short of finding a clean way of marking objects as non dead, I had to touch the code in both projects. In the LIB project, I defined functions rather than global objects. In the app project, I defined functions that called these functions.

What you can do is this:

// Registrations.cpp
#ifdef EXPORT_REGISTRATIONS
#define REGISTRATION_CODE(theClass) \
       void register_##theClass##_function() { \
           // Code for registering your class \
       }
#else
#define REGISTRATION_CODE(theClass) \
       // Declare a function prototype for the function in LIB \
       void register_##theClass##_function(); \
       struct theClass##importer { \
           theClass##importer() { \
               // Call into LIB \
               register_##theClass##_function(); \
           } \
       } g_##theClass##importerInstance; \
#endif

REGISTRATION_CODE(MyClass);
REGISTRATION_CODE(MyOtherClass);

Then in the LIB project, make sure EXPORT_REGISTRATIONS is defined. This will generate the functions that perform the actual registration that you intend to do. In the app project, make sure EXPORT_REGISTRATIONS isn't defined. #include "<path to lib project>\Registrations.cpp" in the app project. This will generate global objects (like your original ones) that call into the functions defined in the LIB project.

This is how I solved my problem. Your mileage may vary. :)

Jörgen Sigvardsson
  • 4,839
  • 3
  • 28
  • 51
  • How can the compiler optimize out a static object when it doesn't know if another translation unit will try to use it? The linker, on the other hand... – Mark Ransom Dec 29 '10 at 18:27
  • i turned off all optimizations - from the libraries and from the app (both compile and link-related ) - it didn't help. If it was linker's optimizations fault - this should help, right? – Piotr Trochim Dec 29 '10 at 19:09
  • I also created a test instance of a class that's exhibiting the problem from the code that gets linked in (I'm able to put a breakpoint there ) - nothing... – Piotr Trochim Dec 29 '10 at 19:11
  • 1
    I went your way - it's less elegant than what I had - but it works :) Thank you very much once again – Piotr Trochim Dec 29 '10 at 22:05
  • 2
    @Paksas: Yes, it's less elegant. :( But at the end of the day, getting it to work is what counts. :) Don't forget to mark my answer as a solution! :) – Jörgen Sigvardsson Dec 30 '10 at 06:46
1

You have defined the a object in two different files. You have violated the one-definition rule, and the compiler is free to do whatever it likes in that situation.

Give one of your variables a different name, and you should be able to observe both of them being initialized.

(The multiple definitions of TestClass are fine for two reasons. One is that they're in separate namespaces, and the other is that even if they weren't in separate namespaces, their definitions would be identical, and the one-definition rule makes an exception for that.)

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • Before the question was edited, the global static objects were part of the namespace as well. – Mark Ransom Dec 29 '10 at 18:30
  • Maybe, @Mark, but maybe not. Perhaps the macro was invoked on two classes that had the same name, thus causing the macro to define them in the same namespace. – Rob Kennedy Dec 29 '10 at 18:35
1

I think the problem is not with the compiler, but with the linker. If it doesn't see any explicit accesses to a symbol in a module in a library, it will not link the module. Thus any static objects in that module will never exist.

To realize why this is so, think back to a time before C++. The purpose of a library was to package up every function you might need into a single file. The linker would go through each module in the library, where a module is defined by compiling a single source file. If the program required a symbol defined in the module, that module would get linked; if not, it would be skipped so that unused parts of the library didn't bloat the application.

This process is recursive, so if module A requires something from module B, module B will be linked even if the program didn't require it directly. This is the key to working around the problem - make sure the main module of your library accesses at least one object or function from every module that contains your static initializers.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • Other static objects from the module get through - there are only a few files in there that are causing the problem – Piotr Trochim Dec 29 '10 at 18:39
  • See also http://stackoverflow.com/questions/4383602/how-to-force-inclusion-of-unused-object-definitions-in-a-library - this explains technicals of the problem quite well. – Suma Dec 29 '10 at 18:44
  • @Paksas, what I'm suggesting is that either all or none of the objects in a file will get linked. Is that what you're seeing? – Mark Ransom Dec 29 '10 at 19:02
  • yes - exactly. Some of the files magically get ommited by the linker - even though all optimizations are off. – Piotr Trochim Dec 29 '10 at 19:12
0

took advantage of the fact that static objects get initialized before main() is called to populate it

I think you are missing the fact that compiler is allowed to defer dynamic initialization of non-local variables with static storage duration until their first use. If your code never uses those registration classes, compiler will not create code to perform dynamic initialization at all.

Gene Bushuyev
  • 5,512
  • 20
  • 19
  • 2
    Sorry, you're wrong "If a variable with static storage duration has initialization or a destructor with side effects, it shall not be eliminated even if it appears to be unused", you're confusing with statics that are local to functions. – Yakov Galka Dec 29 '10 at 18:19
  • You are right, the standard does have this wording, and it was debated in a very long thread before: http://www.velocityreviews.com/forums/t720839-initialization-of-unused-global-variables-from-static-libraries.html Now, whether a bug or not, VC does not dynamically initialize unused objects with static storage duration. – Gene Bushuyev Dec 29 '10 at 18:40
0

Please, drop everything else you're doing and read this now. (Also read the following section, 10.15.)

Second, if by chance you're not falling into that particular problem, could you please show the definition for getClassesRegistry? And have you set a breakpoint there, or used some other diagnostic to verify that that method really isn't being called?

Dan Breslau
  • 11,472
  • 2
  • 35
  • 44
-1

I've found some new facts that can shed the light on the situation and edited the main message.

Try turning off whole program optimization and see what happens. You might have to do something to keep the optimizer from treating this objects as dead code.

Whole solution is compiled in debug mode with all optimizations turned off. The project the files are in is compiled as a static library.

Piotr Trochim
  • 693
  • 5
  • 15