diff --git a/.clang-format b/.clang-format index 25ed77c..4ea37d6 100644 --- a/.clang-format +++ b/.clang-format @@ -1,12 +1,12 @@ --- -Language: Cpp +Language: Cpp # BasedOnStyle: LLVM AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlines: Right -AlignOperands: true +AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false @@ -21,18 +21,18 @@ AlwaysBreakTemplateDeclarations: Yes BinPackArguments: true BinPackParameters: true BraceWrapping: - AfterClass: false + AfterClass: false AfterControlStatement: false - AfterEnum: false - AfterFunction: false - AfterNamespace: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false AfterObjCDeclaration: false - AfterStruct: false - AfterUnion: false + AfterStruct: false + AfterUnion: false AfterExternBlock: false - BeforeCatch: false - BeforeElse: true - IndentBraces: false + BeforeCatch: false + BeforeElse: true + IndentBraces: false SplitEmptyFunction: true SplitEmptyRecord: true SplitEmptyNamespace: true @@ -45,39 +45,39 @@ BreakConstructorInitializersBeforeComma: true BreakConstructorInitializers: BeforeColon BreakAfterJavaFieldAnnotations: false BreakStringLiterals: true -ColumnLimit: 160 -CommentPragmas: '^ IWYU pragma:' +ColumnLimit: 160 +CommentPragmas: "^ IWYU pragma:" CompactNamespaces: false ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: false -DisableFormat: false +DisableFormat: false ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH -IncludeBlocks: Preserve +IncludeBlocks: Preserve IncludeCategories: - - Regex: '^"(llvm|llvm-c|clang|clang-c)/' - Priority: 2 - - Regex: '^(<|"(gtest|gmock|isl|json)/)' - Priority: 3 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '(Test)?$' + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + - Regex: ".*" + Priority: 1 +IncludeIsMainRegex: "(Test)?$" IndentCaseLabels: false IndentPPDirectives: None -IndentWidth: 4 +IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLinesAtTheStartOfBlocks: true -MacroBlockBegin: '' -MacroBlockEnd: '' +MacroBlockBegin: "" +MacroBlockEnd: "" MaxEmptyLinesToKeep: 1 NamespaceIndentation: All ObjCBinPackProtocolList: Auto @@ -93,8 +93,8 @@ PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left -ReflowComments: true -SortIncludes: true +ReflowComments: true +SortIncludes: true SortUsingDeclarations: true SpaceAfterCStyleCast: false SpaceAfterTemplateKeyword: false @@ -106,13 +106,13 @@ SpaceBeforeParens: ControlStatements SpaceBeforeRangeBasedForLoopColon: true SpaceInEmptyParentheses: false SpacesBeforeTrailingComments: 1 -SpacesInAngles: false +SpacesInAngles: false SpacesInContainerLiterals: true SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 4 -UseTab: Never +Standard: Latest +TabWidth: 4 +UseTab: Never ... diff --git a/singleton-smart-pointer-example/CMakeLists.txt b/01-singleton-classic-example/CMakeLists.txt similarity index 51% rename from singleton-smart-pointer-example/CMakeLists.txt rename to 01-singleton-classic-example/CMakeLists.txt index 415dbd2..c29089a 100644 --- a/singleton-smart-pointer-example/CMakeLists.txt +++ b/01-singleton-classic-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-smart-pointer-example VERSION 0.1.0 LANGUAGES CXX) +project(01-singleton-classic-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-smart-pointer-example +add_executable(01-singleton-classic-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-smart-pointer-example PRIVATE +target_include_directories(01-singleton-classic-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-classic-static-example/inc/singleton.h b/01-singleton-classic-example/inc/singleton.h similarity index 96% rename from singleton-classic-static-example/inc/singleton.h rename to 01-singleton-classic-example/inc/singleton.h index e444e66..e0c43e0 100644 --- a/singleton-classic-static-example/inc/singleton.h +++ b/01-singleton-classic-example/inc/singleton.h @@ -10,7 +10,7 @@ class Singleton { return instance; } - void func(); + void info(); private: Singleton(); diff --git a/01-singleton-classic-example/src/main.cpp b/01-singleton-classic-example/src/main.cpp new file mode 100644 index 0000000..f7268fa --- /dev/null +++ b/01-singleton-classic-example/src/main.cpp @@ -0,0 +1,30 @@ +#include "singleton.h" +#include +#include +#include + +/* + * Example with static member variable + * Static memory allocation + * Eager initialization + * Singleton is created before main() and destroyed after main() call + * Initialization is thread-safe - The static member is initialized before main() in a single-threaded context, so no construction race is possible. + * The Static Initialization Order Fiasco - If the singleton instance is accessed during the initialization of another static object. + */ + +int main() { + std::cout << "--- main start ---" << std::endl; + + std::vector threads; + + // Launch 10 threads + for (int i = 0; i < 10; ++i) { + threads.emplace_back([]() { Singleton::getInstance().info(); }); + } + + for (auto& t : threads) { + t.join(); + } + + std::cout << "--- main end ---" << std::endl; +} \ No newline at end of file diff --git a/singleton-classic-static-example/src/singleton.cpp b/01-singleton-classic-example/src/singleton.cpp similarity index 54% rename from singleton-classic-static-example/src/singleton.cpp rename to 01-singleton-classic-example/src/singleton.cpp index dd1c453..209384b 100644 --- a/singleton-classic-static-example/src/singleton.cpp +++ b/01-singleton-classic-example/src/singleton.cpp @@ -1,5 +1,7 @@ #include "singleton.h" #include +#include +#include Singleton Singleton::instance; @@ -7,8 +9,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/singleton-classic-static-example/CMakeLists.txt b/02-singleton-meyers-example/CMakeLists.txt similarity index 50% rename from singleton-classic-static-example/CMakeLists.txt rename to 02-singleton-meyers-example/CMakeLists.txt index 8d6a9db..b63f082 100644 --- a/singleton-classic-static-example/CMakeLists.txt +++ b/02-singleton-meyers-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-classic-static-example VERSION 0.1.0 LANGUAGES CXX) +project(02-singleton-meyers-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-classic-static-example +add_executable(02-singleton-meyers-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-classic-static-example PRIVATE +target_include_directories(02-singleton-meyers-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-meyers-example/inc/singleton.h b/02-singleton-meyers-example/inc/singleton.h similarity index 96% rename from singleton-meyers-example/inc/singleton.h rename to 02-singleton-meyers-example/inc/singleton.h index 581bc25..1e0cffa 100644 --- a/singleton-meyers-example/inc/singleton.h +++ b/02-singleton-meyers-example/inc/singleton.h @@ -10,7 +10,7 @@ class Singleton { return instance; } - void func(); + void info(); private: Singleton(); diff --git a/singleton-meyers-example/src/main.cpp b/02-singleton-meyers-example/src/main.cpp similarity index 85% rename from singleton-meyers-example/src/main.cpp rename to 02-singleton-meyers-example/src/main.cpp index 35e0b57..9dbf7b1 100644 --- a/singleton-meyers-example/src/main.cpp +++ b/02-singleton-meyers-example/src/main.cpp @@ -4,7 +4,7 @@ #include /* - * Meyer’s Singleton - Example with STATIC LOCAL VARIABLE + * Meyer’s Singleton - Example with static local variable * Static memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed after main() call @@ -20,7 +20,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/singleton-meyers-example/src/singleton.cpp b/02-singleton-meyers-example/src/singleton.cpp similarity index 50% rename from singleton-meyers-example/src/singleton.cpp rename to 02-singleton-meyers-example/src/singleton.cpp index bd9c324..61a93b7 100644 --- a/singleton-meyers-example/src/singleton.cpp +++ b/02-singleton-meyers-example/src/singleton.cpp @@ -1,12 +1,14 @@ #include "singleton.h" #include +#include +#include Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/03-singleton-classic-dynamic-example/CMakeLists.txt b/03-singleton-classic-dynamic-example/CMakeLists.txt new file mode 100644 index 0000000..c1dc5b0 --- /dev/null +++ b/03-singleton-classic-dynamic-example/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.21) +project(03-singleton-classic-dynamic-example VERSION 0.1.0 LANGUAGES CXX) + +# Export compile commands for clangd +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_executable(03-singleton-classic-dynamic-example + src/main.cpp + src/singleton.cpp +) + +target_include_directories(03-singleton-classic-dynamic-example PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) \ No newline at end of file diff --git a/singleton-classic-dynamic-example/inc/singleton.h b/03-singleton-classic-dynamic-example/inc/singleton.h similarity index 95% rename from singleton-classic-dynamic-example/inc/singleton.h rename to 03-singleton-classic-dynamic-example/inc/singleton.h index 888e76c..47f1216 100644 --- a/singleton-classic-dynamic-example/inc/singleton.h +++ b/03-singleton-classic-dynamic-example/inc/singleton.h @@ -7,7 +7,7 @@ class Singleton { static Singleton& getInstance(); static void delInstance(); - void func(); + void info(); private: Singleton(); diff --git a/singleton-cherno-example/src/main.cpp b/03-singleton-classic-dynamic-example/src/main.cpp similarity index 72% rename from singleton-cherno-example/src/main.cpp rename to 03-singleton-classic-dynamic-example/src/main.cpp index 4094582..1237d7b 100644 --- a/singleton-cherno-example/src/main.cpp +++ b/03-singleton-classic-dynamic-example/src/main.cpp @@ -2,18 +2,18 @@ #include /* - * Example from Cherno with STATIC GLOBAL VARIABLE + * Example with static member pointer * Dynamic memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed by calling delInstance() - * Thread-safety not guaranteed + * Not Thread-safe */ int main() { std::cout << "--- main start ---" << std::endl; - Singleton::getInstance().func(); - + Singleton::getInstance().info(); + Singleton::getInstance().info(); Singleton::delInstance(); std::cout << "--- main end ---" << std::endl; diff --git a/singleton-classic-dynamic-example/src/singleton.cpp b/03-singleton-classic-dynamic-example/src/singleton.cpp similarity index 75% rename from singleton-classic-dynamic-example/src/singleton.cpp rename to 03-singleton-classic-dynamic-example/src/singleton.cpp index c3f6d80..d3ec175 100644 --- a/singleton-classic-dynamic-example/src/singleton.cpp +++ b/03-singleton-classic-dynamic-example/src/singleton.cpp @@ -1,5 +1,6 @@ #include "singleton.h" #include +#include Singleton* Singleton::instance = nullptr; @@ -21,8 +22,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::cout << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/04-singleton-cherno-example/CMakeLists.txt b/04-singleton-cherno-example/CMakeLists.txt new file mode 100644 index 0000000..cc8d9c5 --- /dev/null +++ b/04-singleton-cherno-example/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.21) +project(04-singleton-cherno-example VERSION 0.1.0 LANGUAGES CXX) + +add_executable(04-singleton-cherno-example + src/main.cpp + src/singleton.cpp +) + +target_include_directories(04-singleton-cherno-example PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/inc +) \ No newline at end of file diff --git a/singleton-cherno-example/inc/singleton.h b/04-singleton-cherno-example/inc/singleton.h similarity index 95% rename from singleton-cherno-example/inc/singleton.h rename to 04-singleton-cherno-example/inc/singleton.h index 60be0e0..9e0e1e7 100644 --- a/singleton-cherno-example/inc/singleton.h +++ b/04-singleton-cherno-example/inc/singleton.h @@ -7,7 +7,7 @@ class Singleton { static Singleton& getInstance(); static void delInstance(); - void func(); + void info(); private: Singleton(); diff --git a/singleton-classic-dynamic-example/src/main.cpp b/04-singleton-cherno-example/src/main.cpp similarity index 69% rename from singleton-classic-dynamic-example/src/main.cpp rename to 04-singleton-cherno-example/src/main.cpp index 5ed9220..b78239e 100644 --- a/singleton-classic-dynamic-example/src/main.cpp +++ b/04-singleton-cherno-example/src/main.cpp @@ -2,18 +2,18 @@ #include /* - * Example with STATIC MEMBER VARIABLE + * Example from Cherno with static global variable in cpp file * Dynamic memory allocation * Lazy initialization * Singleton is created only after first call of getInstance() and destroyed by calling delInstance() - * Thread-safety not guaranteed + * Not Thread-safe */ int main() { std::cout << "--- main start ---" << std::endl; - Singleton::getInstance().func(); - Singleton::getInstance().func(); + Singleton::getInstance().info(); + Singleton::getInstance().info(); Singleton::delInstance(); std::cout << "--- main end ---" << std::endl; diff --git a/singleton-cherno-example/src/singleton.cpp b/04-singleton-cherno-example/src/singleton.cpp similarity index 76% rename from singleton-cherno-example/src/singleton.cpp rename to 04-singleton-cherno-example/src/singleton.cpp index 07d3221..cbcb82e 100644 --- a/singleton-cherno-example/src/singleton.cpp +++ b/04-singleton-cherno-example/src/singleton.cpp @@ -1,5 +1,6 @@ #include "../inc/singleton.h" #include +#include /* it's private to this file */ static Singleton* instance = nullptr; @@ -22,8 +23,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::cout << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/singleton-meyers-example/CMakeLists.txt b/05-singleton-dclp-example/CMakeLists.txt similarity index 53% rename from singleton-meyers-example/CMakeLists.txt rename to 05-singleton-dclp-example/CMakeLists.txt index 1df1f95..8f161c0 100644 --- a/singleton-meyers-example/CMakeLists.txt +++ b/05-singleton-dclp-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-meyers-example VERSION 0.1.0 LANGUAGES CXX) +project(05-singleton-dclp-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-meyers-example +add_executable(05-singleton-dclp-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-meyers-example PRIVATE +target_include_directories(05-singleton-dclp-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/singleton-dclp-example/inc/singleton.h b/05-singleton-dclp-example/inc/singleton.h similarity index 76% rename from singleton-dclp-example/inc/singleton.h rename to 05-singleton-dclp-example/inc/singleton.h index 986de48..4c1d136 100644 --- a/singleton-dclp-example/inc/singleton.h +++ b/05-singleton-dclp-example/inc/singleton.h @@ -4,13 +4,12 @@ class Singleton { public: - Singleton(Singleton& other) = delete; /* Deleted copy constructor. */ + Singleton(Singleton& other) = delete; /* Deleted copy constructor. */ Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ static Singleton& getInstance(); static void delInstance(); - - void func(); + void info(); private: Singleton(); diff --git a/singleton-dclp-example/src/main.cpp b/05-singleton-dclp-example/src/main.cpp similarity index 93% rename from singleton-dclp-example/src/main.cpp rename to 05-singleton-dclp-example/src/main.cpp index 532c03e..5a3629e 100644 --- a/singleton-dclp-example/src/main.cpp +++ b/05-singleton-dclp-example/src/main.cpp @@ -22,7 +22,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/singleton-dclp-example/src/singleton.cpp b/05-singleton-dclp-example/src/singleton.cpp similarity index 67% rename from singleton-dclp-example/src/singleton.cpp rename to 05-singleton-dclp-example/src/singleton.cpp index 9e659de..7c5298b 100644 --- a/singleton-dclp-example/src/singleton.cpp +++ b/05-singleton-dclp-example/src/singleton.cpp @@ -1,16 +1,15 @@ #include "singleton.h" #include +#include +#include Singleton* Singleton::instance = nullptr; std::mutex Singleton::mtx; - Singleton& Singleton::getInstance() { - if (!instance) - { + if (!instance) { std::lock_guard lock(mtx); - if (!instance) - { + if (!instance) { instance = new Singleton(); } } @@ -18,11 +17,9 @@ Singleton& Singleton::getInstance() { } void Singleton::delInstance() { - if (instance) - { + if (instance) { std::lock_guard lock(mtx); - if (instance) - { + if (instance) { delete instance; instance = nullptr; } @@ -33,8 +30,8 @@ Singleton::Singleton() { std::cout << "Singleton created." << std::endl; } -void Singleton::func() { - std::cout << "Doing something..." << std::endl; +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; } Singleton::~Singleton() { diff --git a/singleton-dclp-example/CMakeLists.txt b/06-singleton-leaky-example/CMakeLists.txt similarity index 53% rename from singleton-dclp-example/CMakeLists.txt rename to 06-singleton-leaky-example/CMakeLists.txt index b7f4e2a..cbbc7e8 100644 --- a/singleton-dclp-example/CMakeLists.txt +++ b/06-singleton-leaky-example/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.21) -project(singleton-dclp-example VERSION 0.1.0 LANGUAGES CXX) +project(06-singleton-leaky-example VERSION 0.1.0 LANGUAGES CXX) # Export compile commands for clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -add_executable(singleton-dclp-example +add_executable(06-singleton-leaky-example src/main.cpp src/singleton.cpp ) -target_include_directories(singleton-dclp-example PRIVATE +target_include_directories(06-singleton-leaky-example PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/inc ) \ No newline at end of file diff --git a/06-singleton-leaky-example/inc/singleton.h b/06-singleton-leaky-example/inc/singleton.h new file mode 100644 index 0000000..23923ad --- /dev/null +++ b/06-singleton-leaky-example/inc/singleton.h @@ -0,0 +1,19 @@ +#pragma once + +class Singleton { +public: + Singleton(const Singleton&) = delete; /* Deleted copy constructor. */ + Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ + + static Singleton& getInstance() { + /* Static local variable is thread safe >= C++11 */ + static Singleton* instance = new Singleton(); + return *instance; + } + + void info(); + +private: + Singleton(); + ~Singleton(); // This will NEVER be called +}; \ No newline at end of file diff --git a/singleton-smart-pointer-example/src/main.cpp b/06-singleton-leaky-example/src/main.cpp similarity index 59% rename from singleton-smart-pointer-example/src/main.cpp rename to 06-singleton-leaky-example/src/main.cpp index 585cca8..9131602 100644 --- a/singleton-smart-pointer-example/src/main.cpp +++ b/06-singleton-leaky-example/src/main.cpp @@ -4,11 +4,11 @@ #include /* - * Example with STATIC LOCAL VARIABLE that is a SMART POINTER + * Leaky Singleton - Example with static local pointer * Dynamic memory allocation * Lazy initialization - * Singleton is created in the first call of getInstance() and destroyed automatically after main() call - * Thread-safety IS guaranteed (C++11+) + * Singleton is created only after first call of getInstance() and never destroyed. + * Initialization is Thread-safe - Thread safety is guaranteed since C++11. */ int main() { @@ -18,7 +18,7 @@ int main() { // Launch 10 threads for (int i = 0; i < 10; ++i) { - threads.emplace_back([]() { Singleton::getInstance().func(); }); + threads.emplace_back([]() { Singleton::getInstance().info(); }); } for (auto& t : threads) { diff --git a/06-singleton-leaky-example/src/singleton.cpp b/06-singleton-leaky-example/src/singleton.cpp new file mode 100644 index 0000000..61a93b7 --- /dev/null +++ b/06-singleton-leaky-example/src/singleton.cpp @@ -0,0 +1,16 @@ +#include "singleton.h" +#include +#include +#include + +Singleton::Singleton() { + std::cout << "Singleton created." << std::endl; +} + +void Singleton::info() { + std::osyncstream(std::cout) << "Current instance address: " << this << " | Current thread ID: " << std::this_thread::get_id() << '\n'; +} + +Singleton::~Singleton() { + std::cout << "Singleton destroyed." << std::endl; +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index ccf6896..8c62a08 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ project(cpp-singleton-pattern-examples LANGUAGES CXX ) -add_subdirectory(singleton-cherno-example) -add_subdirectory(singleton-meyers-example) -add_subdirectory(singleton-classic-static-example) -add_subdirectory(singleton-classic-dynamic-example) -add_subdirectory(singleton-dclp-example) -add_subdirectory(singleton-smart-pointer-example) \ No newline at end of file +add_subdirectory(01-singleton-classic-example) +add_subdirectory(02-singleton-meyers-example) +add_subdirectory(03-singleton-classic-dynamic-example) +add_subdirectory(04-singleton-cherno-example) +add_subdirectory(05-singleton-dclp-example) +add_subdirectory(06-singleton-leaky-example) \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index 12a570f..4994fb9 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -5,7 +5,6 @@ "minor": 21, "patch": 0 }, - "configurePresets": [ { "name": "base-config", @@ -13,12 +12,11 @@ "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "CMAKE_CXX_STANDARD": "11", + "CMAKE_CXX_STANDARD": "20", "CMAKE_CXX_STANDARD_REQUIRED": "ON", "CMAKE_CXX_EXTENSIONS": "OFF" } }, - { "name": "linux-ninja-debug", "displayName": "Linux (Ninja) - Debug", @@ -33,7 +31,6 @@ "CMAKE_BUILD_TYPE": "Debug" } }, - { "name": "windows-msvc", "displayName": "Windows (VS 2022) - Debug", @@ -45,7 +42,6 @@ }, "generator": "Visual Studio 17 2022" }, - { "name": "windows-ninja-debug", "displayName": "Windows (Ninja + GCC) - Debug", @@ -61,7 +57,6 @@ } } ], - "buildPresets": [ { "name": "linux-ninja-debug", @@ -80,4 +75,4 @@ "jobs": 0 } ] -} +} \ No newline at end of file diff --git a/README.md b/README.md index e3e34c4..1a1cf93 100644 --- a/README.md +++ b/README.md @@ -1,64 +1,80 @@ # Singleton pattern examples in C++ -A collection of minimal, self-contained C++ examples demonstrating multiple ways to implement the Singleton design pattern. The repository includes modern, thread-safe techniques (Meyers Singleton) and legacy aproaches (Raw Pointers, Double-Checked Locking) for comparison. +A collection of minimal, self-contained C++ examples demonstrating multiple ways to implement the Singleton design pattern. The repository includes modern, thread-safe techniques (Meyer's Singleton) and legacy aproaches (Double-Checked Locking) for comparison. ## πŸ” Overview -### ⭐ singleton-meyers-example -* Meyers Singleton β€” the simplest and safest modern C++ singleton implementation. -* 🧩 Static local variable -* πŸ’Ύ Static memory allocation -* ⏳ Lazy initialization -* 🧼 Automatic cleanup (destroyed after main() exits) -* πŸ”’ Thread-safe since C++11 -* A function-local static variable is initialized exactly once, even in a multi-threaded environment. -* 🟒 This is the best and simplest way to implement a singleton in C++11 and later. - -### ⭐ singleton-cherno-example -* Cherno-style Singleton - https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY -* 🧩 Static global variable -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization -* 🧹 Manual destruction required (via delInstance()) -* ⚠️ Not thread-safe -* πŸ”΄ Suitable only for single-threaded - -### ⭐ singleton-classic-static-example -* Singleton with a static member instance β€” created eagerly at program startup. -* 🧩 Static member variable -* πŸ’Ύ Static memory allocation -* ⚑ Eager initialization (constructed before main() starts) -* 🧼 Automatic destruction after main() exits -* ⚠️ Not thread-safe -* πŸ”΄ Can suffer from the static initialization order fiasco - -### ⭐ singleton-classic-dynamic-example -* Singleton with a static member pointer β€” dynamically allocated on first use. -* 🧩 Static member pointer -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization (created only on first call to getInstance()) -* 🧹 Requires manual destruction via delInstance() -* ⚠️ Not thread-safe -* πŸ”΄ Not recommended for multi-threaded applications - -### ⭐ singleton-dclp-example -* Double-Checked Locking Pattern (DCLP) β€” classic but unsafe lazy-initialization pattern. -* 🧩 Static member pointer -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization -* 🧹 Destroyed manually via delInstance() -* ⚠️ Not thread-safe in C++ β€” suffers from data races and reordering issues -* ❌ DCLP is unreliable because multiple threads may observe a partially constructed object -* β›” Obsoleted by C++11 (local static initialization is the correct modern solution) -* Reference: [https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004](https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf) - -### ⭐ singleton-smart-pointer-example -* Singleton using a static local smart pointer -* 🧩 Static local variable -* πŸ’Ύ Dynamic memory allocation -* ⏳ Lazy initialization (instance created on first getInstance() call) -* 🧼 Automatically destroyed after main() exits -* πŸ”’ Thread-safe initialization (C++11+) +### 1️⃣ singleton-classic-example + +* πŸ”³ **Singleton with a static member instance.** +* 🧩 **Static member variable** +* πŸ’Ύ **Static memory allocation** +* ⚑ **Eager initialization** β†’ Created before `main` starts +* 🧼 **Automatic cleanup** β†’ Destroyed after `main` exits +* πŸ”’ **Initialization is Thread-safe** + * The static member is initialized before `main` in a single-threaded context, so no construction race is possible. +* ⚠️ **Static Initialization Order Fiasco** + * Can suffer from SIOF If the singleton instance is accessed during the initialization of another static object, it may lead to UB due to the order of initialization. +* ⚠️ **Static Destruction Order Fiasco** + * A symmetric problem, If one static object's destructor calls another static that has been destroyed, it results in UB. + +### 2️⃣ singleton-meyers-example + +* πŸ”³ **Meyer's Singleton** β†’ The simplest and safest modern C++ singleton implementation. +* 🧩 **Static local variable** +* πŸ’Ύ **Static memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧼 **Automatic cleanup** β†’ Destroyed after `main` exits +* πŸ”’ **Initialization is Thread-safe since C++11** + * A function-local static variable is initialized exactly once, even in a multi-threaded environment. +* ⚠️ The Meyer's Singleton fixes **Static Initialization Order Fiasco**, but can suffer from **Static Destruction Order Fiasco**. + +### 3️⃣ singleton-classic-dynamic-example +* **Singleton with a static member pointer** β†’ Dynamically allocated on first use. +* 🧩 **Static member pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** + * Two threads could call `delInstance()` simultaneously, or one calls `getInstance()` while another calls `delInstance()`, leading to a race on the pointer. +* ⚠️ **Static Destruction Order Fiasco** + * If another static's destructor calls `getInstance()` after `delInstance()` has run, you're dereferencing a deleted pointer, right back to UB. +* ❗ Not recommended for multi-threaded applications. + + +### 4️⃣ singleton-cherno-example +* **Cherno-style Singleton** β†’ [Why I don't like Singletons - Youtube](https://youtu.be/IMZMLvIwa-k?si=Q__9r--DOre6jahY) +* 🧩 **Static global variable** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** β†’ Same as [singleton-classic-dynamic-example](#3️⃣-singleton-classic-dynamic-example) +* ❗ Not recommended for multi-threaded applications. + +### 5️⃣ singleton-dclp-example +* **Singleton with Double-Checked Locking Pattern (DCLP)** β†’ Classic but unsafe lazy-initialization pattern. +* 🧩 **Static member pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **Manual cleanup** β†’ Requires manual destruction via `delInstance()` +* ⚠️ **Not Thread-safe** β†’ Suffers from data races and reordering issues. +* ❌ **DCLP is unreliable** β†’ Multiple threads may observe a partially constructed object. +* β›” **Obsoleted by C++11** β†’ Local static initialization is the correct modern solution. +* Reference: [C++ and the Perils of Double-Checked Locking by Scott Meyers and Andrei Alexandrescu](https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf) + +### 6️⃣ singleton-leaky-example +* πŸ”³ **"Leaky" Singleton** β†’ A heap-allocated Meyer's Singleton. +* 🧩 **Static local pointer** +* πŸ’Ύ **Dynamic memory allocation** +* ⏳ **Lazy initialization** β†’ Created only on first call to `getInstance()` +* 🧹 **No cleanup** β†’ You are intentionally leaking the memory. + * When the process ends, the Operating System reclaims the entire memory block anyway. "Leaking" at process exit is technically harmless. +* πŸ”’ **Initialization is Thread-safe since C++11** + * The local static pointer initialization is still protected by C++11's thread-safe static init guarantee, so the `new` only fires once, safely. +* ❎ **Avoids Static Initialization/Destruction Order Fiasco** + * Since the destructor is never called, it can't try to access other dead objects during shutdown. + +--- ## βš™οΈ Prerequisites @@ -139,24 +155,24 @@ cmake --build --preset --target Example: ```bash -cmake --build --preset linux-ninja-debug --target singleton-meyers-example +cmake --build --preset linux-ninja-debug --target 02-singleton-meyers-example ``` ## πŸƒ Running Examples ### πŸ–₯️ Windows (MSVC) ```bash -build/windows-msvc/singleton-meyers-example/Debug/singleton-meyers-example.exe +build/windows-msvc/singleton-meyers-example/Debug/02-singleton-meyers-example.exe ``` ### πŸ–₯️ Windows (MinGW) ```bash -build/windows-ninja-debug/singleton-meyers-example/singleton-meyers-example.exe +build/windows-ninja-debug/singleton-meyers-example/02-singleton-meyers-example.exe ``` ### 🐧 Linux ```bash -./build/linux-ninja-debug/singleton-meyers-example/singleton-meyers-example +./build/linux-ninja-debug/singleton-meyers-example/02-singleton-meyers-example ``` diff --git a/singleton-cherno-example/CMakeLists.txt b/singleton-cherno-example/CMakeLists.txt deleted file mode 100644 index 7993ba1..0000000 --- a/singleton-cherno-example/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(singleton-cherno-example VERSION 0.1.0 LANGUAGES CXX) - -add_executable(singleton-cherno-example - src/main.cpp - src/singleton.cpp -) - -target_include_directories(singleton-cherno-example PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/inc -) \ No newline at end of file diff --git a/singleton-classic-dynamic-example/CMakeLists.txt b/singleton-classic-dynamic-example/CMakeLists.txt deleted file mode 100644 index ae938e0..0000000 --- a/singleton-classic-dynamic-example/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 3.21) -project(singleton-classic-dynamic-example VERSION 0.1.0 LANGUAGES CXX) - -# Export compile commands for clangd -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -add_executable(singleton-classic-dynamic-example - src/main.cpp - src/singleton.cpp -) - -target_include_directories(singleton-classic-dynamic-example PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/inc -) \ No newline at end of file diff --git a/singleton-classic-static-example/src/main.cpp b/singleton-classic-static-example/src/main.cpp deleted file mode 100644 index bcf4992..0000000 --- a/singleton-classic-static-example/src/main.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "singleton.h" -#include - -/* - * Example with STATIC MEMBER VARIABLE - * Static memory allocation - * Eager initialization - * Singleton is created before main() and destroyed after main() call - * Thread-safety not guaranteed - */ - -int main() { - std::cout << "--- main start ---" << std::endl; - - Singleton::getInstance().func(); - - std::cout << "--- main end ---" << std::endl; -} \ No newline at end of file diff --git a/singleton-smart-pointer-example/inc/singleton.h b/singleton-smart-pointer-example/inc/singleton.h deleted file mode 100644 index eb4db03..0000000 --- a/singleton-smart-pointer-example/inc/singleton.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -class Singleton { -public: - Singleton(const Singleton&) = delete; /* Deleted copy constructor. */ - Singleton& operator=(const Singleton&) = delete; /* Deleted copy assigment operator. */ - - static Singleton& getInstance(); - void func(); - ~Singleton(); - -private: - Singleton(); -}; \ No newline at end of file diff --git a/singleton-smart-pointer-example/src/singleton.cpp b/singleton-smart-pointer-example/src/singleton.cpp deleted file mode 100644 index c186def..0000000 --- a/singleton-smart-pointer-example/src/singleton.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "singleton.h" -#include -#include - -Singleton& Singleton::getInstance() { - // The "Magic Static" is thread-safe in C++11. - static std::unique_ptr instance(new Singleton()); - return *instance; -} - -Singleton::Singleton() { - std::cout << "Singleton created." << std::endl; -} - -void Singleton::func() { - std::cout << "Doing something..." << std::endl; -} - -Singleton::~Singleton() { - std::cout << "Singleton destroyed." << std::endl; -} \ No newline at end of file