diff options
-rw-r--r-- | common.cmake | 96 | ||||
-rw-r--r-- | std_call_once_bug/CMakeLists.txt | 5 | ||||
-rw-r--r-- | std_call_once_bug/README.md | 27 | ||||
-rw-r--r-- | std_call_once_bug/sample.cpp | 131 |
4 files changed, 259 insertions, 0 deletions
diff --git a/common.cmake b/common.cmake new file mode 100644 index 0000000..d719542 --- /dev/null +++ b/common.cmake @@ -0,0 +1,96 @@ +# Copyright (c) 2017 Egor Tensin <Egor.Tensin@gmail.com> +# It's a CMake code snippet I use in all of my CMake projects. +# It makes targets link the runtime statically by default + strips debug +# symbols in release builds. +# The latest version can be found at +# https://gist.github.com/egor-tensin/cmake-common. +# Distributed under the MIT License. + +# Version: 2017-05-19T13:51:22+00:00 + +get_directory_property(parent_directory PARENT_DIRECTORY) +set(is_root_project $<NOT:parent_directory>) + +set(USE_STATIC_RUNTIME "${is_root_project}" CACHE BOOL "Link the runtime statically") +set(STRIP_SYMBOL_TABLE "${is_root_project}" CACHE BOOL "Strip symbol tables") + +if(is_root_project) + if(MSVC) + add_compile_options(/MP /W4) + elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + add_compile_options(-Wall -Wextra) + endif() +endif() + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +function(use_static_runtime_msvc target) + get_target_property(target_type "${target}" TYPE) + if(target_type STREQUAL INTERFACE_LIBRARY) + else() + target_compile_options("${target}" PRIVATE + $<$<CONFIG:Debug>:/MTd> + $<$<NOT:$<CONFIG:Debug>>:/MT>) + endif() +endfunction() + +function(use_static_runtime_gcc target) + get_target_property(target_type "${target}" TYPE) + if(target_type STREQUAL EXECUTABLE) + target_link_libraries("${target}" PRIVATE -static) + endif() +endfunction() + +function(use_static_runtime target) + if(MSVC) + use_static_runtime_msvc("${target}") + elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + use_static_runtime_gcc("${target}") + else() + message(WARNING "Unrecognized toolset") + endif() +endfunction() + +function(strip_symbol_table_gcc target) + get_target_property(target_type "${target}" TYPE) + set(release_build $<OR:$<CONFIG:Release>,$<CONFIG:MinSizeRel>>) + if(target_type STREQUAL INTERFACE_LIBRARY) + else() + target_link_libraries("${target}" PRIVATE $<${release_build}:-s>) + endif() +endfunction() + +function(strip_symbol_table target) + if(MSVC) + elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + strip_symbol_table_gcc("${target}") + else() + message(WARNING "Unrecognized toolset") + endif() +endfunction() + +function(apply_common_settings target) + if(TARGET "${target}") + get_target_property(target_imported "${target}" IMPORTED) + if(target_imported STREQUAL NOTFOUND OR NOT target_imported) + if(STRIP_SYMBOL_TABLE) + strip_symbol_table("${target}") + endif() + if(USE_STATIC_RUNTIME) + use_static_runtime("${target}") + endif() + endif() + endif() +endfunction() + +macro(add_executable target) + _add_executable(${ARGV}) + apply_common_settings("${target}") +endmacro() + +macro(add_library target) + _add_library(${ARGV}) + apply_common_settings("${target}") +endmacro() diff --git a/std_call_once_bug/CMakeLists.txt b/std_call_once_bug/CMakeLists.txt new file mode 100644 index 0000000..31d45de --- /dev/null +++ b/std_call_once_bug/CMakeLists.txt @@ -0,0 +1,5 @@ +project(std_call_once_bug CXX) + +include(../common.cmake) + +add_executable(sample sample.cpp) diff --git a/std_call_once_bug/README.md b/std_call_once_bug/README.md new file mode 100644 index 0000000..c42f191 --- /dev/null +++ b/std_call_once_bug/README.md @@ -0,0 +1,27 @@ +std::call_once bug in Visual C++ 2012/2013 +========================================== + +Code samples from the post "std::call_once bug in Visual C++ 2012/2013". + +Building +-------- + +Create the build files using CMake and build using your native build tools +(Visual Studio/make/etc.). + +In the example below, the project directory is +"C:\workspace\personal\cpp-notes" and Visual Studio 2013 is used, targeting +x86. + + > cmake -G "Visual Studio 12 2013" C:\workspace\personal\cpp-notes\std_call_once_bug + ... + + > cmake --build . --config release + ... + +See also +-------- + +* [License] + +[License]: ../README.md#license diff --git a/std_call_once_bug/sample.cpp b/std_call_once_bug/sample.cpp new file mode 100644 index 0000000..22a63c6 --- /dev/null +++ b/std_call_once_bug/sample.cpp @@ -0,0 +1,131 @@ +// Copyright (c) 2016 Egor Tensin <Egor.Tensin@gmail.com> +// This file is part of the "C++ notes" project. +// For details, see https://github.com/egor-tensin/cpp-notes. +// Distributed under the MIT License. + +#include <ctime> + +#include <chrono> +#include <iostream> +#include <mutex> +#include <sstream> +#include <string> +#include <thread> + +namespace +{ + template <typename Derived> + class Singleton + { + public: + static Derived& get_instance() + { + std::call_once(initialized_flag, &initialize_instance); + return Derived::get_instance_unsafe(); + } + + protected: + Singleton() = default; + ~Singleton() = default; + + static Derived& get_instance_unsafe() + { + static Derived instance; + return instance; + } + + private: + static void initialize_instance() + { + Derived::get_instance_unsafe(); + } + + static std::once_flag initialized_flag; + + Singleton(const Singleton&) = delete; + Singleton& operator=(const Singleton&) = delete; + }; + + template <typename Derived> + std::once_flag Singleton<Derived>::initialized_flag; + + class Logger : public Singleton<Logger> + { + public: + Logger& operator<<(const char*) + { + return *this; + } + + private: + Logger() + { + std::this_thread::sleep_for(std::chrono::seconds{3}); + } + + ~Logger() = default; + + friend class Singleton<Logger>; + }; + + class Duke : public Singleton<Duke> + { + private: + Duke() + { + Logger::get_instance() << "started Duke's initialization"; + std::this_thread::sleep_for(std::chrono::seconds{10}); + Logger::get_instance() << "finishing Duke's initialization"; + } + + ~Duke() = default; + + friend class Singleton<Duke>; + }; + + std::mutex timestamp_mtx; + + std::string get_timestamp() + { + std::lock_guard<std::mutex> lck{timestamp_mtx}; + const auto tt = std::time(NULL); + return std::ctime(&tt); + } + + void entered(const char* f) + { + std::ostringstream oss; + oss << "Entered " << f << " at " << get_timestamp(); + std::cout << oss.str(); + } + + void exiting(const char* f) + { + std::ostringstream oss; + oss << "Exiting " << f << " at " << get_timestamp(); + std::cout << oss.str(); + } + + void get_logger() + { + entered(__FUNCTION__); + Logger::get_instance(); + exiting(__FUNCTION__); + } + + void get_duke() + { + entered(__FUNCTION__); + Duke::get_instance(); + exiting(__FUNCTION__); + } +} + +int main() +{ + std::thread t1{&get_duke}; + std::thread t2{&get_logger}; + t1.join(); + t2.join(); + return 0; +} |