Implement thread-safe singleton classes using Curiously Recurring Template Pattern (CRTP)
Option | Default | Description |
---|---|---|
SINGLETON_INJECT_ABSTRACT_CLASS |
OFF |
Prevent construction of derived class at compile time |
SINGLETON_INSTALL |
OFF |
Install headers and CMake targets |
cmake --list-presets all # List all CMake presets
cmake --preset windows # Configure
cmake --build --preset windows # Build
ctest --preset windows # Test
cmake --build --preset windows -t install # Install
include(FetchContent)
set(SINGLETON_INJECT_ABSTRACT_CLASS ON)
set(SINGLETON_INSTALL ON)
FetchContent_Declare(
singleton
URL https://github.com/jimmy-park/singleton/archive/main.tar.gz
)
FetchContent_MakeAvailable(singleton)
# If you're using CPM.cmake
# CPMAddPackage(
# NAME singleton
# URL https://github.com/jimmy-park/singleton/archive/main.tar.gz
# OPTIONS
# "SINGLETON_INJECT_ABSTRACT_CLASS ON"
# "SINGLETON_INSTALL ON"
# )
add_executable(main main.cpp)
target_link_libraries(main PRIVATE
singleton::singleton # C++11
singleton::singleton-dclp # C++17
singleton::singleton-atomic # C++20
)
Rely on initialization of static local variable
#include <singleton.hpp>
struct Foo : public Singleton<Foo> {
void Bar() {}
};
int main()
{
// Compile error when SINGLETON_INJECT_ABSTRACT_CLASS is ON
// Foo foo;
Foo::GetInstance().Bar();
}
Implement based on Double-Checked Locking Pattern (DCLP)
Use this version when you need to initialize with parameters or control the destruction order manually
- C++17 features
- Inline static member variable
- If statements with initializer
std::shared_mutex
#include <singleton_dclp.hpp>
class Foo : public SingletonDclp<Foo> {
public:
Foo(int n) : n_ { n } {}
void Bar() {}
private:
int n_;
};
int main()
{
Foo::Construct(17);
Foo::GetInstance()->Bar();
Foo::Destruct();
}
GetInstance()
must be called betweenConstruct()
andDestruct()
- Don't forget to call
Destruct()
before terminating program
Use std::atomic::wait()
to block GetInstance()
during construction
#include <singleton_atomic.hpp>
class Foo : public SingletonAtomic<Foo> {
public:
Foo(int n) {}
void Bar() {}
};
int main()
{
std::jthread t { [] {
std::this_thread::sleep_for(std::chrono::seconds { 1 });
Foo::Construct(20);
} }; // Construct in another thread
Foo::GetInstance()->Bar(); // Block until construction is finished
}