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();
}
};