Search code examples
c++debuggingvisual-c++extern

Extern pointer is null in static library, worked fine when not a static library


I broke my Visual Studio project (2015, C++) into three pieces:

  • The main application in a static library
  • The executable which just has a main function
  • Unit tests which use the static .lib file so that they can import the required classes and do the unit tests on them.

Before I split it into a lib/exe/tests, the main application was just a standalone executable, and it worked perfectly fine.

Now I can't run the executable with only the main function, nor the unit tests as a certain pointer is always null. The only main difference is that I'm using a raw pointer in this example, but I was using a unique_ptr in my code (currently however I switched to the raw pointer just to make sure this following example is as accurate as possible and it didn't magically compile/run properly with the raw pointer).

The code looks very similar to the following (the extra code has been removed):

// console.hpp
extern Console *console;

The implementation cpp file (only of what is needed):

// console.cpp
Console *console = new Console();

Now in some unrelated function, this code fails due to the console pointer being a nullptr:

// some_other_file.cpp
#include "console.hpp"

    // Inside some function...
    console->doSomething();  // console is NULL

Again, the code I have worked perfectly fine when it was in one project. Regardless, it all compiles fine with no linking errors even though it has been broken into 3 pieces, but now that pointer is always null.

As a last interesting note, the non-pointer variables that are global and extern do work. Is this something limited to pointers/unique_ptrs?

Is this something fixable by a singleton pattern?


Solution

  • The clue is in this comment: "it appears other code is getting called before the main function that don't let the global variable get initialized."

    The code referencing console is probably running as part of the initialization of another global and, in this case, is happening before the initialization of console. You have to be very careful to be sure that you're not depending on the order of the global initializers. You were probably getting lucky before you split the program, and now your luck has run out.

    The easiest way to fix it is to use a singleton pattern. Instead of having other parts of the program directly reference the pointer, you have them call a function that returns the pointer, and that function will initialize it the first time. This ensures that it's initialized before it's used.

    Here's a common pattern for it:

    Console *GetConsole() {
        static Console *console = new Console();
        return console;
    }
    

    Now console is no longer a global variable. Everything that wants to access the console calls GetConsole instead.

    The function-local static variable will be initialized the first time the function is called, and after that it just returns the value. If you have multiple threads and an older compiler, you have to do more work to protect against a possible race condition. Modern compilers should do the initialization in a thread-safe way.