C++ static member variable is not initialized if it's in an static library

84 views Asked by At

I want to implement a self-register pattern in c++. My goal is to have one file defining a class and the class can self-register to a registry. My implmentation goes like this

https://godbolt.org/z/qGT7sfWdK

#include <functional>
#include <memory>
#include <span>
#include <string>
#include <vector>
#include <iostream>

class ToolBase
{
public:
    virtual ~ToolBase(){}

    virtual std::vector<std::string> GetToolMenuItem() = 0;
};

std::vector<std::function<std::unique_ptr<ToolBase>()>>& GetRegistry();

std::vector<std::function<std::unique_ptr<ToolBase>()>>& GetRegistry()
{
    static std::vector<std::function<std::unique_ptr<ToolBase>()>> registered;
    return registered;
}

class ActualTool : public ToolBase
{
public:
    ActualTool(){(void)_register;} // guess this is not really needed for non-template 
register method
    ~ActualTool() override {};

public:
    std::vector<std::string> GetToolMenuItem() override
    {
        return {"Something"};
    }

    static bool _register;
    static bool Register()
    {
        GetRegistry().push_back([](){return std::unique_ptr<ToolBase>(new ActualTool());});
        return true;
    }
};

bool ActualTool::_register = ActualTool::Register();

int main(){

    std::cout << GetRegistry().size();
}

It works fine on godbolt (I assume it's a single translation unit), but my design goal is to have a standalone cpp file of the ActualTool which is exactly like what gtest does. I have read gtest's source code and see it's using a very similar method. But in my implementation (separate cpp files) I can't have _register initialized. I am not sure what I'm missing here

Update: Here is how to reproduce it in a fresh environment. I am using Apple Clang 14.0.3


// main.cpp - executable
#include <iostream>
#include "Editor.hpp"

int main(int argc, const char * argv[]) {
    Editor e;
    e.Print(); // expect to print 1, but 0
}

// the following files are compiled into a static lib which is linked with the executable

// Tool.hpp
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <iostream>
class Tool
{
public:
    virtual ~Tool(){}

    virtual std::vector<std::string> GetToolMenuItem() = 0;
};

std::vector<std::function<std::unique_ptr<Tool>()>>& GetRegistry();

// Tool.cpp
#include "Tool.hpp"

std::vector<std::function<std::unique_ptr<Tool>()>>& GetRegistry()
{
    static std::vector<std::function<std::unique_ptr<Tool>()>> registry;
    return registry;
}

// ActualTool.cpp
#include "Tool.hpp"

class ActualTool : public Tool
{
public:
    ActualTool(){(void)_register;} // guess this is not really needed for non-template register method
    
    ~ActualTool() override {};

public:
    std::vector<std::string> GetToolMenuItem() override
    {
        return {"Something"};
    }

    static bool _register;
    static bool Register()
    {
        GetRegistry().push_back([](){return std::unique_ptr<Tool>(new ActualTool());});
        return true;
    }
};

bool ActualTool::_register = ActualTool::Register();

// Editor.hpp
#pragma once
#include <stdio.h>
#include "Tool.hpp"

class Editor
{
public:
    void Print()
    {
        std::cout << GetRegistry().size();
    }
};


0

There are 0 answers