0

I have the following testing program:

// manager.h
#pragma once

#include <list>

namespace testapp
{
    class Manager
    {
    public:
        static Manager& instance();
        void addItem(int val);

    private:
        std::list<int> m_items;
    };

    template <int N>
    class Helper
    {
    public:
        Helper()
        {
            Manager::instance().addItem(N);
        }
    };
}

// manager.cpp
#include "manager.h"

namespace testapp
{
    static Manager s_global_instance;
    Manager& Manager::instance()
    {
        return s_global_instance;
    }

    void Manager::addItem(int val)
    {
        m_items.push_back(val);
    }
}

// main.cpp
#include <iostream>
#include "manager.h"

namespace testapp
{
    Helper<1> helper;
}

int main()
{
    std::cout << "It works!\n";
}

When I build this app with the following command line:

g++ main.cpp manager.cpp -std=c++2a -o TestApp

I got Segmentation fault, which is triggered in m_items.push_back(val).

However, if I build this app with different source order:

g++ manager.cpp main.cpp -std=c++2a -o TestApp

The program works as expected. I can the output It works! without any error.

After some investigation, I found that the problem is s_global_instance seems not fully initialized when Helper<1> helper gets instantiated. What I mean here is the member std::list<int> m_items is not initialized (properly) when global static variable s_global_instance is used. When addItem gets called, I stepped into push_back, I found that the head node is not actually initialized. It gets initialized after addItem getting called.

If I change static Manager s_global_instance to a local static variable like this:

Manager& Manager::instance()
{
   static Manager _instance;
   return _instance;
}

It's always working. No segmentation fault no matter which command line I use as showing above. The source file order doesn't matter any more.

My questions are here:

  • Why compiling order matters when global static variable is used?
  • Why local static variable works?
  • Is this issue related to template instantiation?
  • What is the initialization order when static variable, global static variable, global variable and template involved?
6
  • There's a phrase, static initialization order fiasco and I suspect the file order influences the initialization order here. But yeah, the usual way is to have your singleton be a local static variable of the singleton getter function. Commented Nov 25, 2022 at 9:17
  • Thanks @MarcusMüller. Yes, the proper solution here is to use local static variable instead of global static variable. I have the same feeling of the initialization issue here caused by the compiling order, but can't well explain what actually happens here. This link is very helpful and the video in the link probably has more insight. Thanks again for your input. Commented Nov 25, 2022 at 9:30
  • What actually happens is pretty simple. You have two global variables, s_global_instance and helper, defined in two different translation units. Both have non-trivial constructors. These constructors are guaranteed to be called some time before main, but it's unspecified which one is called first. When helper happens to be constructed first, its constructor calls a method on s_global_instance whose lifetime hasn't started yet, whereupon the program exhibits undefined behavior. Commented Nov 25, 2022 at 14:59
  • A local static variable is initialized on first use, when control passes over its definition for the first time (this initialization is automatically thread-safe, the compiler inserts necessary synchronization; not that it matters in your example). So helper constructor runs and calls Manager::instance() which in turn causes _instance to be constructed. Commented Nov 25, 2022 at 15:03
  • No, this has nothing to do with templates. You can make Helper a non-template class that just hard-codes addItem(1), and should observe the same behavior. Commented Nov 25, 2022 at 15:05

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.