diff --git a/.conanfile.txt b/.conanfile.txt
new file mode 100644
index 0000000..18f3c8e
--- /dev/null
+++ b/.conanfile.txt
@@ -0,0 +1,2 @@
+[requires]
+gtest/1.8.1@bincrafters/stable
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6d504a0..f693399 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,28 +2,83 @@ cmake_minimum_required(VERSION 3.10) # For CXX_STANDARD 17 property on Visual St
project(WaitFreeHashMap)
enable_language(CXX)
-include(cmake/common.cmake)
-include(cmake/get_external_dependency.cmake)
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+
+include(ctulu)
+include(conan)
+include(setup_output_dirs)
set(CMAKE_DEBUG_POSTFIX _d)
# By default build in Release mode
-if( NOT CMAKE_BUILD_TYPE )
+if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
endif()
-get_filename_component(root_dir ${CMAKE_SOURCE_DIR} ABSOLUTE)
+get_filename_component(root_dir ${CMAKE_CURRENT_SOURCE_DIR} ABSOLUTE)
get_filename_component(include_dir ${root_dir}/include ABSOLUTE)
-make_target(WaitFreeHashMapLibrary "WaitFreeHashMap" INCLUDES ${include_dir} OPTIONS cxx interface)
+get_filename_component(tests_dir ${root_dir}/tests ABSOLUTE)
+get_filename_component(example_dir ${root_dir}/examples ABSOLUTE)
+
+# set parameter INTERFACE since the library is header only.
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ message(STATUS "Activating warning on library")
+ ctulu_create_target(WaitFreeCollections "WaitFreeCollections" INTERFACE_INCLUDES ${include_dir} CXX 17 INTERFACE)
+else()
+ ctulu_create_target(WaitFreeCollections "WaitFreeCollections" INTERFACE_INCLUDES SYSTEM ${include_dir} CXX 17 INTERFACE)
+endif()
+
+option(WFC_BUILD_EXAMPLES "Build project examples" OFF)
+option(WFC_BUILD_TESTS "Build project tests" OFF)
+option(WFC_BUILD_CLANG_FORMAT_TARGET "Build clang-format target" OFF)
+option(WFC_BUILD_ALL "Activate all previous options" OFF)
+
+if (WFC_BUILD_ALL)
+ set(WFC_BUILD_EXAMPLES ON)
+ set(WFC_BUILD_TESTS ON)
+ set(WFC_BUILD_CLANG_FORMAT_TARGET ON)
+endif()
+
+if (WFC_BUILD_EXAMPLES)
+ find_package(Threads REQUIRED)
+
+ ctulu_create_target(UnorderedMapExample1 "WaitFreeCollections" FILES ${example_dir}/unordered_map_example.cpp CXX 17 EXECUTABLE W_LEVEL 2)
+ ctulu_target_warning_from_file(UnorderedMapExample1 ${root_dir}/cmake/warnings.txt)
+ target_link_libraries(UnorderedMapExample1 Threads::Threads WaitFreeCollections)
+endif()
-find_package(Threads REQUIRED)
-get_external_dependency(googletest external_libs/google_test.cmake)
+if (WFC_BUILD_CLANG_FORMAT_TARGET)
+ set(DIRS "${include_dir}")
+ if (BUILD_TESTS)
+ list(APPEND DIRS ${test_dir})
+ endif()
+ if (BUILD_EXAMPLES)
+ list(APPEND DIRS ${example_dir})
+ endif()
-make_target(Sample1 "WaitFreeHashMap" example/main.cpp OPTIONS cxx executable)
-set_property(TARGET Sample1 PROPERTY CXX_STANDARD 17)
-target_link_libraries(Sample1 ${CMAKE_THREAD_LIBS_INIT} WaitFreeHashMapLibrary gtest_main)
+ ctulu_generate_clang_format("Clang-format" DIRS ${DIRS})
+endif()
+
+if (CMAKE_BUILD_TYPE MATCHES "Debug")
+ set(WFC_BUILD_TESTS ON)
+endif()
+
+if (WFC_BUILD_TESTS)
+ message(STATUS "Enabling testing")
+ enable_testing()
+
+ find_package(Threads REQUIRED)
+
+ conan_check(REQUIRED)
+ conan_cmake_run(CONANFILE .conanfile.txt BASIC_SETUP CMAKE_TARGETS BUILD missing)
+
+ ctulu_create_target(UnorderedMapTests "WaitFreeCollections" DIRS ${tests_dir}/unordered_map/ TEST CXX 17 W_LEVEL 2)
+ ctulu_target_warning_from_file(UnorderedMapTests ${root_dir}/cmake/warnings.txt)
+ target_link_libraries(UnorderedMapTests WaitFreeCollections CONAN_PKG::gtest Threads::Threads)
+
+ ctulu_create_target(UtilityTests "WaitFreeCollections" DIRS ${tests_dir}/utility/ TEST CXX 17 W_LEVEL 2)
+ ctulu_target_warning_from_file(UtilityTests ${root_dir}/cmake/warnings.txt)
+ target_link_libraries(UtilityTests WaitFreeCollections CONAN_PKG::gtest)
+endif()
-make_target(unit_tests "WaitFreeHashMap" test/single_thread_test.cpp OPTIONS cxx executable)
-set_property(TARGET unit_tests PROPERTY CXX_STANDARD 17)
-target_link_libraries(unit_tests WaitFreeHashMapLibrary gtest_main)
-add_test(NAME example_test COMMAND unit_tests)
\ No newline at end of file
+#set(CMAKE_VERBOSE_MAKEFILE 1)
diff --git a/README.md b/README.md
index b665d1c..56fe5d9 100644
--- a/README.md
+++ b/README.md
@@ -1,15 +1,183 @@
-# WaitFreeHashMap
+# WaitFreeCollections
+
+
+
+ master branch |
+
+
+ |
+
+
+ develop branch |
+
+
+ |
+
+
+ License |
+
+
+ |
+
+
+
+A header-only library providing wait-free collections such as hash map and double ended queue (deque).
+
+## Wait-Free Hash Map
+
+An implementation of a wait-free hash map as described in the article
-An implementation of an hashmap described in the article:
> P. Laborde, S. Feldman et D. Dechev, « A Wait-Free Hash Map »,
> International Journal of Parallel Programming, t. 45, n o 3, p. 421-448, 2017, issn : 1573-7640.
> doi : https://doi.org/10.1007/s10766-015-0376-3
-# Copyright
+See [unordered_map example](./examples/unordered_map_example.cpp) in `examples` folder.
-License: MIT
+## Double ended queue (Deque)
+
+WIP
-# Requirements
+## Requirements
- Compiler which supports C++17
- CMake ≥ 3.10
+
+## Minimal unordered map example
+
+```cpp
+#include
+#include
+
+#include
+
+constexpr int nbr_threads = 16;
+
+int main()
+{
+ wfc::unordered_map m(4, nbr_threads, nbr_threads);
+
+ std::array threads;
+ for (std::size_t i = 0; i < nbr_threads; ++i)
+ {
+ threads[i] = std::thread([&m, i]() {
+ m.insert(i, i);
+ });
+ }
+
+ for (auto& t: threads)
+ {
+ t.join();
+ }
+
+ m.visit([](std::pair p) {
+ std::cout << '[' << p.first << '-' << p.second << "]\n";
+ });
+
+ return 0;
+}
+```
+
+This should output:
+
+```
+[0-0]
+[1-1]
+[2-2]
+[3-3]
+[4-4]
+[5-5]
+[6-6]
+[7-7]
+[8-8]
+[9-9]
+[10-10]
+[11-11]
+[12-12]
+[13-13]
+[14-14]
+[15-15]
+```
+
+## How to import the library using CMake
+
+To include the library, you may copy / paste the content of this repository in a subfolder (such as `externals`) or use a git submodule.
+
+Then, in you CMakeLists.txt file, you just have to do something similar to:
+
+```cmake
+cmake_minimum_required(VERSION 3.10)
+project(MyProject)
+enable_language(CXX)
+set(CMAKE_CXX_STANDARD 17)
+
+add_subdirectory(externals/WaitFreeCollections)
+
+find_package(Threads REQUIRED) # you'll probably need that one too
+
+add_executable(MyTarget src/main.cpp)
+target_link_libraries(MyTarget Threads::Threads WaitFreeCollections)
+```
+
+At this point, the project hierarchy looks like:
+
+```
+|- CMakeLists.txt
+|- src/
+ |- main.cpp
+|- externals/
+ |- WaitFreeCollections/
+ |- ...
+```
+
+You may try to use the minimal example above as your `main.cpp`.
+
+Also, since this is a header-only library, you may copy / paste the content of the include folder in your
+project to achieve similar results (maybe a little bit less clean though).
+
+## Build targets
+
+You don't need to actually build the library beforehand to use it in your project due to the "header-only" nature of it.
+The build targets in this repository are for tests, exemples and code formatting.
+These are good to know should you contribute to this project or play with the exemples.
+
+First, you need to create a sub-directory to run `cmake` from it. Then you can build using `make`.
+
+```
+$ mkdir build
+$ cmake .. -DWFC_BUILD_ALL=1
+```
+
+By default, everything is build in Release mode. You can pass `-DCMAKE_BUILD_TYPE=Debug` to `cmake` to ask Debug builds.
+This will also activate useful features for developing purposes such as the holy warnings.
+The `-DWFC_BUILD_ALL=1` parameter will tell CMake to include all our targets in the build process.
+
+CMake will generate several targets that you can build separately using the `--target` parameter:
+
+- `UnorderedMapExample1`
+- `UtilityTests`
+- `UnorderedMapTests`
+- `Clang-format`
+
+For instance, to build the `UnorderedMapTests` run
+
+```
+$ cmake --build . --target UnorderedMapTests
+```
+
+You can also build everything by not giving any specific target.
+Produced executables are inside the `bin` folder.
+
+Note that the `Clang-format` target does not produce anything.
+It just run the clang formatter.
+Also this target needs to be called explicitly.
+
+For more details, see the [CMakeLists.txt](CMakeLists.txt) file.
+
+## Copyright
+
+License: MIT
+
+Main contributors:
+
+- [Jérôme Boulmier](https://github.com/Lomadriel)
+- [Benoît Cortier](https://github.com/CBenoit)
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..62cc4dd
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,35 @@
+image: ubuntu
+
+install:
+ # install conan
+ - sudo pip install conan --upgrade
+
+before_build:
+ # update gcc and g++ alternatives
+ - sudo update-alternatives --set gcc /usr/bin/gcc-8
+
+ - gcc --version
+ - g++ --version
+ - cmake --version
+
+ - mkdir ${APPVEYOR_BUILD_FOLDER}/debug_build
+ - cd ${APPVEYOR_BUILD_FOLDER}/debug_build
+ - cmake .. -DWFC_BUILD_ALL=1 -DCMAKE_BUILD_TYPE=Debug
+
+ - mkdir ${APPVEYOR_BUILD_FOLDER}/release_build
+ - cd ${APPVEYOR_BUILD_FOLDER}/release_build
+ - cmake .. -DWFC_BUILD_ALL=1 -DCMAKE_BUILD_TYPE=Release
+
+build_script:
+ - cd ${APPVEYOR_BUILD_FOLDER}/debug_build
+ - make
+
+ - cd ${APPVEYOR_BUILD_FOLDER}/release_build
+ - make
+
+test_script:
+ - ${APPVEYOR_BUILD_FOLDER}/debug_build/bin/UnorderedMapTests
+ - ${APPVEYOR_BUILD_FOLDER}/debug_build/bin/UtilityTests
+ - ${APPVEYOR_BUILD_FOLDER}/release_build/bin/UnorderedMapTests
+ - ${APPVEYOR_BUILD_FOLDER}/release_build/bin/UtilityTests
+
diff --git a/cmake/common.cmake b/cmake/common.cmake
deleted file mode 100644
index dbdf8da..0000000
--- a/cmake/common.cmake
+++ /dev/null
@@ -1,1115 +0,0 @@
-##################################################################################
-# MIT License #
-# #
-# Copyright (c) 2017 Maxime Pinard #
-# #
-# Permission is hereby granted, free of charge, to any person obtaining a copy #
-# of this software and associated documentation files (the "Software"), to deal #
-# in the Software without restriction, including without limitation the rights #
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell #
-# copies of the Software, and to permit persons to whom the Software is #
-# furnished to do so, subject to the following conditions: #
-# #
-# The above copyright notice and this permission notice shall be included in all #
-# copies or substantial portions of the Software. #
-# #
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR #
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, #
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE #
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER #
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, #
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE #
-# SOFTWARE. #
-##################################################################################
-
-include(CheckCXXCompilerFlag)
-include(CheckCCompilerFlag)
-
-## check_variable_name(function_name var_names... USED used_var_names...)
-# Check if variables passed to a function don't have names conflicts with variables used in the function.
-# Generate a fatal error on variable name conflict.
-# {value} [in] function_name: Function name
-# {value} [in] var_names: Names of the variables passed as parameters of the function
-# {value} [in] used_var_names: Names of the function internal variables
-function(check_variable_name _function_name)
- split_args(_var_names USED _used_var_names ${ARGN})
- foreach(_var_name ${_var_names})
- foreach(_used_var_name ${_used_var_names})
- if("${_var_name}" STREQUAL "${_used_var_name}")
- message(FATAL_ERROR "parameter ${_var_name} passed to function ${_function_name} has the same name than an internal variable of the function and can't be accessed")
- endif()
- endforeach()
- endforeach()
-endfunction()
-
-## directory_is_empty(output dir)
-# Check if a directory is empty.
-# If the input directory doesn't exist or is not a directory, it is considered as empty.
-# {variable} [out] output: true if the directory is empty, false otherwise
-# {value} [in] dir: Directory to check
-function(directory_is_empty output dir)
- set(tmp_output false)
- get_filename_component(dir_path ${dir} REALPATH)
- if(EXISTS "${dir_path}")
- if(IS_DIRECTORY "${dir_path}")
- file(GLOB files "${dir_path}/*")
- list(LENGTH files len)
- if(len EQUAL 0)
- set(tmp_output true)
- endif()
- else()
- set(tmp_output true)
- endif()
- else()
- set(tmp_output true)
- endif()
- set(${output} ${tmp_output} PARENT_SCOPE)
-endfunction()
-
-## split_args(left delimiter right args...)
-# Split arguments into left and right parts on delimiter token.
-# {variable} [out] left: Arguments at the left (before) the delimiter
-# {value} [in] delimiter: Delimiter of the left and right part
-# {variable} [out] right: Arguments at the right (after) the delimiter
-# {value} [in] args: Arguments to split
-function(split_args left delimiter right)
- set(delimiter_found false)
- set(tmp_left)
- set(tmp_right)
- foreach(it ${ARGN})
- if("${it}" STREQUAL ${delimiter})
- set(delimiter_found true)
- elseif(delimiter_found)
- list(APPEND tmp_right ${it})
- else()
- list(APPEND tmp_left ${it})
- endif()
- endforeach()
- set(${left} ${tmp_left} PARENT_SCOPE)
- set(${right} ${tmp_right} PARENT_SCOPE)
-endfunction()
-
-## has_item(output item args...)
-# Determine if arguments list contains an item.
-# {variable} [out] output: true if the list contains the item, false otherwise
-# {value} [in] item: Item to search in the list
-# {value} [in] args: Arguments list which may contain the item
-function(has_item output item)
- set(tmp_output false)
- foreach(it ${ARGN})
- if("${it}" STREQUAL "${item}")
- set(tmp_output true)
- break()
- endif()
- endforeach()
- set(${output} ${tmp_output} PARENT_SCOPE)
-endfunction()
-
-## flatten(property)
-# Remove leading, trailing and multiple from a property.
-# Useful to prevent unnecessary rebuilds.
-# {variable} [in,out] property: Property to flatten
-function(flatten _property)
- # variables names are prefixed with'_' lower chances of parent scope variable hiding
- check_variable_name(flatten ${_property} USED _property _tmp_property)
- string(REPLACE " " " " _tmp_property "${${_property}}")
- string(STRIP "${_tmp_property}" _tmp_property)
- set(${_property} "${_tmp_property}" PARENT_SCOPE)
-endfunction()
-
-## manage_flag(property regexp flag)
-# Add a flag to a property or replace an existing flag matching a regular expression.
-# If a flag of the property matches the regular expression, it is replaced by the input flag,
-# else, the input flag is added to the property.
-# Update the property in the cache.
-# {variable} [in] property: Property to manage
-# {value} [in] regexp: Regular expression to match
-# {value} [in] flag: Flag to add (or use to replace regexp)
-function(manage_flag _property _regexp _flag)
- # variables names are prefixed with'_' lower chances of parent scope variable hiding
- check_variable_name(manage_flag ${_property} USED _property _regexp _flag)
- if("${${_property}}" MATCHES ${_regexp})
- string(REGEX REPLACE ${_regexp} ${_flag} ${_property} "${${_property}}")
- else()
- set(${_property} "${${_property}} ${_flag}")
- endif()
- flatten(${_property})
- set(${_property} "${${_property}}" CACHE STRING "" FORCE)
-endfunction()
-
-## add_flag(property flag [configs...])
-# If not already present, add a flag to a property for the specified configs.
-# {value} [in] property: Property to change (|(EXE|MODULE|SHARED|STATIC)_LINKER)
-# {value} [in] value: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(add_flag property flag)
- if(ARGN)
- foreach(config ${ARGN})
- manage_flag(CMAKE_${property}_FLAGS_${config} ${flag} ${flag})
- endforeach()
- else()
- manage_flag(CMAKE_${property}_FLAGS ${flag} ${flag})
- endif()
-endfunction()
-
-## remove_flag(property regexp [configs...])
-# Remove an existing flag of a property matching a regular expression for the specified configs.
-# {value} [in] property: Property to change (|(EXE|MODULE|SHARED|STATIC)_LINKER)
-# {value} [in] regexp: Regular expression to match
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(remove_flag property regexp)
- if(ARGN)
- foreach(config ${ARGN})
- manage_flag(CMAKE_${property}_FLAGS_${config} ${regexp} " ")
- endforeach()
- else()
- manage_flag(CMAKE_${property}_FLAGS ${regexp} " ")
- endif()
-endfunction()
-
-## add_linker_flag(flag [configs...])
-# Add a flag to the linker arguments property for the specified configs.
-# {value} [in] flag: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(add_linker_flag flag)
- foreach(item IN ITEMS EXE MODULE SHARED STATIC)
- add_flag(${item}_LINKER ${flag} ${ARGN})
- endforeach()
-endfunction()
-
-## remove_linker_flag(flag [configs...])
-# Remove a flag from the linker arguments property for the specified configs.
-# {value} [in] flag: Flag to remove
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(remove_linker_flag regexp)
- foreach(item IN ITEMS EXE MODULE SHARED STATIC)
- remove_flag(${item}_LINKER ${regexp} ${ARGN})
- endforeach()
-endfunction()
-
-## add_cx_flag(flag [configs...])
-# Add a flag to the C and CXX compilers arguments property for the specified configs.
-# {value} [in] flag: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(add_cx_flag flag)
- CHECK_CXX_COMPILER_FLAG(${flag} has${flag})
- if(has${flag})
- add_flag(C ${flag} ${ARGN})
- add_flag(CXX ${flag} ${ARGN})
- endif()
-endfunction()
-
-## remove_cx_flag(flag [configs...])
-# Remove a flag from the C and CXX compilers arguments property for the specified configs.
-# {value} [in] flag: Flag to remove
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(remove_cx_flag regexp)
- remove_flag(C ${regexp} ${ARGN})
- remove_flag(CXX ${regexp} ${ARGN})
-endfunction()
-
-## group_files(group root files...)
-# Group files in IDE project generation (by calling source_group) relatively to a root.
-# {value} [in] group: Group, files will be grouped in
-# {value} [in] root: Root, files will be grouped relative to it
-# {value} [in] files: Files to group
-function(group_files group root)
- foreach(it ${ARGN})
- get_filename_component(dir ${it} PATH)
- file(RELATIVE_PATH relative ${root} ${dir})
- set(local ${group})
- if(NOT "${relative}" STREQUAL "")
- set(local "${group}/${relative}")
- endif()
- # replace '/' and '\' (and repetitions) by '\\'
- string(REGEX REPLACE "[\\\\\\/]+" "\\\\\\\\" local ${local})
- source_group("${local}" FILES ${it})
- endforeach()
-endfunction()
-
-## filter_list(keep list regexp...)
-# Include/exclude items from a list matching one or more regular expressions.
-# Include: keep only the list items that matches regular expressions.
-# Exclude: keep only the list items that doesn't matches regular expressions.
-# {value} [in] keep: 1 for include, 0 for exclude
-# {variable} [in,out] list: List to filter
-# {value} [in] regexp: Regular expressions which list items may match
-function(filter_list _keep _list)
- # variables names are prefixed with'_' lower chances of parent scope variable hiding
- check_variable_name(filter_list ${_list} USED _list _tmp_list)
- set(_tmp_list)
- foreach(_it ${${_list}})
- set(_touch)
- foreach(_regexp ${ARGN})
- if(${_it} MATCHES ${_regexp})
- set(_touch true)
- break()
- endif()
- endforeach()
- if((_keep EQUAL 1 AND DEFINED _touch) OR (_keep EQUAL 0 AND NOT DEFINED _touch))
- list(APPEND _tmp_list ${_it})
- endif()
- endforeach()
- set(${_list} ${_tmp_list} PARENT_SCOPE)
-endfunction()
-
-## filter_out(list regexp...)
-# Exclude items from a list matching one or more regular expressions.
-# Exclude: keep only the list items that doesn't matches regular expressions.
-# {variable} [in,out] list: List to filter
-# {value} [in] regexp: Regular expressions which list items may match
-macro(filter_out list)
- filter_list(0 ${list} ${ARGN})
-endmacro()
-
-## filter_in(list regexp...)
-# Include items from a list matching one or more regular expressions.
-# Include: keep only the list items that matches regular expressions.
-# {variable} [in,out] list: List to filter
-# {value} [in] regexp: Regular expressions which list items may match
-macro(filter_in list)
- filter_list(1 ${list} ${ARGN})
-endmacro()
-
-## join_list(output sep items...)
-# Join items with a separator into a variable.
-# {variable} [out] output: Output variable, contain the item joined
-# {value} [in] sep: Separator to insert between items
-# {value} [in] items: Items to join
-function(join_list output sep)
- set(tmp_output)
- set(first)
- foreach(it ${ARGN})
- if(NOT DEFINED first)
- set(tmp_output ${it})
- set(first true)
- else()
- set(tmp_output "${tmp_output}${sep}${it}")
- endif()
- endforeach()
- set(${output} ${tmp_output} PARENT_SCOPE)
-endfunction()
-
-## get_files(output_files directories... [OPTIONS [recurse]])
-# Get (recursively or not) C and C++ sources files form input directories.
-# Also group the files into Sources with the group_files function relatively to their root directory.
-# {variable} [out] output: Output variable, contain the sources files
-# {value} [in] directories: Directory to search files
-# {option} [in] recurse: If present, search is recursive
-function(get_files output)
- split_args(dirs "OPTIONS" options ${ARGN})
- set(glob GLOB)
- has_item(has_recurse "recurse" ${options})
- if(has_recurse)
- set(glob GLOB_RECURSE)
- endif()
- set(files)
- foreach(it ${dirs})
- if(IS_DIRECTORY ${it})
- set(patterns
- "${it}/*.c"
- "${it}/*.cc"
- "${it}/*.cpp"
- "${it}/*.cxx"
- "${it}/*.h"
- "${it}/*.hpp"
- )
- file(${glob} tmp_files ${patterns})
- list(APPEND files ${tmp_files})
- get_filename_component(parent_dir ${it} DIRECTORY)
- group_files(Sources "${parent_dir}" ${tmp_files})
- else()
- list(APPEND files ${it})
- get_filename_component(dir ${it} DIRECTORY)
- group_files(Sources "${dir}" ${it})
- endif()
- endforeach()
- set(${output} ${files} PARENT_SCOPE)
-endfunction()
-
-## target_add_includes(target includes...)
-# Add input includes (files or directories) to target.
-# {value} [in] target: Target to add includes
-# {value} [in] includes: Includes to add
-function(target_add_includes target)
- list(LENGTH ARGN size)
- if(NOT ${size} GREATER 0)
- return()
- endif()
- list(REMOVE_DUPLICATES ARGN)
- list(SORT ARGN)
- target_include_directories(${target} PRIVATE ${ARGN})
-endfunction()
-
-## target_add_system_includes(target includes...)
-# Add input system includes (files or directories) to target.
-# {value} [in] target: Target to add includes
-# {value} [in] includes: Includes to add
-function(target_add_system_includes target)
- list(LENGTH ARGN size)
- if(NOT ${size} GREATER 0)
- return()
- endif()
- list(REMOVE_DUPLICATES ARGN)
- list(SORT ARGN)
- target_include_directories(${target} SYSTEM PRIVATE ${ARGN})
-endfunction()
-
-## target_add_compile_definition(target definition [configs...])
-# Add a private compile definition to the target for the specified configs.
-# {value} [in] target: Target to add flag
-# {value} [in] definition: Definition to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(target_add_compile_definition target definition)
- if(${ARGC} GREATER 2)
- foreach(config ${ARGN})
- string(TOLOWER "${config}" config_lower)
- set(config_name)
- foreach(valid_config IN ITEMS "Debug" "RelWithDebInfo" "Release")
- string(TOLOWER "${valid_config}" valid_config_lower)
- if(${config_lower} STREQUAL ${valid_config_lower})
- set(config_name ${valid_config})
- endif()
- endforeach()
- if(DEFINED config_name)
- target_compile_definitions(${target} PRIVATE "$<$:${definition}>")
- endif()
- endforeach()
- else()
- target_compile_definitions(${target} PRIVATE "${definition}")
- endif()
-endfunction()
-
-## target_add_compiler_flag(target flag [configs...])
-# Add a flag to the compiler arguments of the target for the specified configs.
-# Add the flag only if the compiler support it (checked with CHECK_CXX_COMPILER_FLAG).
-# {value} [in] target: Target to add flag
-# {value} [in] flag: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(target_add_compiler_flag target flag)
- CHECK_CXX_COMPILER_FLAG(${flag} has${flag})
- if(has${flag})
- if(${ARGC} GREATER 2)
- foreach(config ${ARGN})
- string(TOLOWER "${config}" config_lower)
- set(config_name)
- foreach(valid_config IN ITEMS "Debug" "RelWithDebInfo" "Release")
- string(TOLOWER "${valid_config}" valid_config_lower)
- if(${config_lower} STREQUAL ${valid_config_lower})
- set(config_name ${valid_config})
- endif()
- endforeach()
- if(DEFINED config_name)
- target_compile_options(${target} PRIVATE "$<$:${flag}>")
- endif()
- endforeach()
- else()
- target_compile_options(${target} PRIVATE "${flag}")
- endif()
- endif()
-endfunction()
-
-## append_to_target_property(target property [values...])
-# Append values to a target property.
-# {value} [in] target: Target to modify
-# {value} [in] property: Property to append values to
-# {value} [in] values: Values to append to the property
-function(append_to_target_property target property)
- set(new_values ${ARGN})
- get_target_property(existing_values ${target} ${property})
- if(existing_values)
- set(new_values "${existing_values} ${new_values}")
- endif()
- set_target_properties(${target} PROPERTIES ${property} ${new_values})
-endfunction()
-
-## __target_link_flag_property(target flag [configs...])
-# Add a flag to the linker arguments of the target for the specified configs using LINK_FLAGS properties of the target.
-# Function made for CMake 3.12 or less, future CMake version will have target_link_options() with cmake-generator-expressions.
-# {value} [in] target: Target to add flag
-# {value} [in] flag: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(__target_link_flag_property target flag)
- if(${ARGC} GREATER 2)
- foreach(config ${ARGN})
- append_to_target_property(${target} LINK_FLAGS_${config} ${flag})
- endforeach()
- else()
- append_to_target_property(${target} LINK_FLAGS ${flag})
- endif()
-endfunction()
-
-## target_add_linker_flag(target flag [configs...])
-# Add a flag to the linker arguments of the target for the specified configs.
-# Add the flag only if the linker support it (checked with CHECK_CXX_COMPILER_FLAG).
-# {value} [in] target: Target to add flag
-# {value} [in] flag: Flag to add
-# {value} [in] configs: Configs for the property to change (DEBUG|RELEASE|RELWITHDEBINFO)
-function(target_add_linker_flag target flag)
- CHECK_CXX_COMPILER_FLAG(${flag} has${flag})
- if(has${flag})
- if(${ARGC} GREATER 2)
- foreach(config ${ARGN})
- string(TOLOWER "${config}" config_lower)
- set(config_name)
- foreach(valid_config IN ITEMS "Debug" "RelWithDebInfo" "Release")
- string(TOLOWER "${valid_config}" valid_config_lower)
- if(${config_lower} STREQUAL ${valid_config_lower})
- set(config_name ${valid_config})
- endif()
- endforeach()
- if(DEFINED config_name)
- if(COMMAND target_link_options)
- target_link_options(${target} PRIVATE "$<$:${flag}>")
- else()
- string(TOUPPER "${config_name}" config_name_upper)
- __target_link_flag_property(${target} ${flag} ${config_name_upper})
- endif()
- endif()
- endforeach()
- else()
- if(COMMAND target_link_options)
- target_link_options(${target} PRIVATE "${flag}")
- else()
- __target_link_flag_property(${target} "${flag}")
- endif()
- endif()
- endif()
-endfunction()
-
-## target_set_output_directory(target directory)
-# Set the target runtime, library and archive output directory to the input directory.
-# {value} [in] target: Target to configure
-# {value} [in] directory: Output directory
-function(target_set_output_directory target directory)
- target_set_runtime_output_directory(${target} "${directory}")
- target_set_library_output_directory(${target} "${directory}")
- target_set_archive_output_directory(${target} "${directory}")
-endfunction()
-
-## target_set_runtime_output_directory(target directory)
-# Set the target runtime output directory to the input directory.
-# {value} [in] target: Target to configure
-# {value} [in] directory: Output directory
-function(target_set_runtime_output_directory target directory)
- set_target_properties(${target} PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY "${directory}"
- RUNTIME_OUTPUT_DIRECTORY_DEBUG "${directory}"
- RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${directory}"
- RUNTIME_OUTPUT_DIRECTORY_RELEASE "${directory}"
- )
-endfunction()
-
-## target_set_library_output_directory(target directory)
-# Set the target library output directory to the input directory.
-# {value} [in] target: Target to configure
-# {value} [in] directory: Output directory
-function(target_set_library_output_directory target directory)
- set_target_properties(${target} PROPERTIES
- LIBRARY_OUTPUT_DIRECTORY "${directory}"
- LIBRARY_OUTPUT_DIRECTORY_DEBUG "${directory}"
- LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${directory}"
- LIBRARY_OUTPUT_DIRECTORY_RELEASE "${directory}"
- )
-endfunction()
-
-## target_set_archive_output_directory(target directory)
-# Set the target archive output directory to the input directory.
-# {value} [in] target: Target to configure
-# {value} [in] directory: Output directory
-function(target_set_archive_output_directory target directory)
- set_target_properties(${target} PROPERTIES
- ARCHIVE_OUTPUT_DIRECTORY "${directory}"
- ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${directory}"
- ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${directory}"
- ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${directory}"
- )
-endfunction()
-
-## setup_msvc(target [OPTIONS [static_runtime] [no_warnings] [low_warnings]])
-# Set up msvc-specific options of the target.
-# Without options: maximum warnings are enabled.
-# {value} [in] target: Target to configure
-# {option} [in] static_runtime: If present, C runtime library is statically linked
-# {option} [in] no_warnings: If present, warnings are disabled (useful for external projects)
-# {option} [in] low_warnings: If present, low/normal warnings are enabled
-function(setup_msvc target)
- split_args(ignore "OPTIONS" options ${ARGN})
- has_item(option_static_runtime "static_runtime" ${options})
- has_item(option_no_warnings "no_warnings" ${options})
- has_item(option_low_warnings "low_warnings" ${options})
-
- # set Source and Executable character sets to UTF-8
- target_add_compiler_flag(${target} "/utf-8")
-
- # enable parallel compilation
- target_add_compiler_flag(${target} "/MP")
-
- # generates complete debugging information
- target_add_compiler_flag(${target} "/Zi" DEBUG RELWITHDEBINFO)
- target_add_linker_flag(${target} "/DEBUG:FULL" DEBUG RELWITHDEBINFO)
-
- # set optimization
- target_add_compiler_flag(${target} "/Od" DEBUG)
- target_add_compiler_flag(${target} "/O2" RELWITHDEBINFO)
- target_add_compiler_flag(${target} "/Ox" RELEASE)
-
- # enables automatic parallelization of loops
- target_add_compiler_flag(${target} "/Qpar" RELEASE)
-
- # enable runtime checks
- target_add_compiler_flag(${target} "/RTC1" DEBUG)
-
- # disable incremental compilations
- target_add_linker_flag(${target} "/INCREMENTAL:NO" RELEASE RELWITHDEBINFO)
-
- # remove unused symbols
- target_add_linker_flag(${target} "/OPT:REF" RELEASE RELWITHDEBINFO)
- target_add_linker_flag(${target} "/OPT:ICF" RELEASE RELWITHDEBINFO)
-
- # disable manifests
- target_add_linker_flag(${target} "/MANIFEST:NO" RELEASE RELWITHDEBINFO)
-
- # enable function-level linking
- #target_add_compiler_flag(${target} "/Gy" RELEASE RELWITHDEBINFO)
-
- # sets the Checksum in the .exe header
- #target_add_linker_flag(${target} "/RELEASE" RELEASE RELWITHDEBINFO)
-
- # statically link C runtime library to static_runtime targets
- if(option_static_runtime)
- target_add_compiler_flag(${target} "/MTd" DEBUG)
- target_add_compiler_flag(${target} "/MT" RELWITHDEBINFO RELEASE)
- else()
- target_add_compiler_flag(${target} "/MDd" DEBUG)
- target_add_compiler_flag(${target} "/MD" RELWITHDEBINFO RELEASE)
- endif()
-
- # manage warnings
- set(flags)
- if(option_no_warnings)
- set(flags "/W0")
- elseif(option_low_warnings)
- set(flags "/W3")
- else()
- set(flags
- ## Base flags:
- "/W4"
-
- ## Extra flags:
- "/w44263" # 'function': member function does not override any base class virtual member function
- "/w44265" # 'class': class has virtual functions, but destructor is not virtual
- "/w44287" # 'operator': unsigned/negative constant mismatch
- "/w44289" # nonstandard extension used : 'var' : loop control variable declared in the for-loop is used outside the for-loop scope
- "/w44296" # 'operator': expression is always false
- "/w44355" # 'this' : used in base member initializer list
- "/w44365" # 'action': conversion from 'type_1' to 'type_2', signed/unsigned mismatch
- "/w44412" # 'function': function signature contains type 'type'; C++ objects are unsafe to pass between pure code and mixed or native
- "/w44431" # missing type specifier - int assumed. Note: C no longer supports default-int
- "/w44536" # 'type name': type-name exceeds meta-data limit of 'limit' characters
- "/w44545" # expression before comma evaluates to a function which is missing an argument list
- "/w44546" # function call before comma missing argument list
- "/w44547" # 'operator': operator before comma has no effect; expected operator with side-effect
- "/w44548" # expression before comma has no effect; expected expression with side-effect
- "/w44549" # 'operator': operator before comma has no effect; did you intend 'operator'?
- "/w44555" # expression has no effect; expected expression with side-effect
- "/w44619" # #pragma warning: there is no warning number 'number'
- #"/w44623" # 'derived class': default constructor could not be generated because a base class default constructor is inaccessible
- #"/w44625" # 'derived class': copy constructor could not be generated because a base class copy constructor is inaccessible
- #"/w44626" # 'derived class': assignment operator could not be generated because a base class assignment operator is inaccessible
- #"/w44640" # 'instance': construction of local static object is not thread-safe
- "/w44917" # 'declarator': a GUID can only be associated with a class, interface, or namespace
- "/w44946" # reinterpret_cast used between related classes: 'class1' and 'class2'
- "/w44986" # 'symbol': exception specification does not match previous declaration
- "/w44987" # nonstandard extension used: 'throw (...)'
- "/w44988" # 'symbol': variable declared outside class/function scope
- "/w45022" # 'type': multiple move constructors specified
- "/w45023" # 'type': multiple move assignment operators specified
- "/w45029" # nonstandard extension used: alignment attributes in C++ apply to variables, data members and tag types only
- "/w45031" # #pragma warning(pop): likely mismatch, popping warning state pushed in different file
- "/w45032" # detected #pragma warning(push) with no corresponding #pragma warning(pop)
- "/w45034" # use of intrinsic 'intrinsic' causes function function to be compiled as guest code
- "/w45035" # use of feature 'feature' causes function function to be compiled as guest code
- "/w45036" # varargs function pointer conversion when compiling with /hybrid:x86arm64 'type1' to 'type2'
- "/w45038" # data member 'member1' will be initialized after data member 'member2'
- "/w45039" # 'function': pointer or reference to potentially throwing function passed to extern C function under -EHc. Undefined behavior may occur if this function throws an exception.
- "/w45042" # 'function': function declarations at block scope cannot be specified 'inline' in standard C++; remove 'inline' specifier
-
- ## Apocalypse flags:
- #"/Wall"
- #"/WX"
- )
- endif()
- foreach(flag ${flags})
- target_add_compiler_flag(${target} ${flag})
- endforeach()
-endfunction()
-
-## setup_gcc(target [OPTIONS [static_runtime] [c] [cxx] [no_warnings] [low_warnings]])
-# Set up gcc-specific options of the target.
-# Without options: maximum warnings are enabled.
-# {value} [in] target: Target to configure
-# {option} [in] static_runtime: If present, C runtime library is statically linked
-# {option} [in] c: If present, the target is written in C, add warnings if no warnings option is specified
-# {option} [in] cxx: If present, the target is written in C++, add warnings if no warnings option is specified
-# {option} [in] no_warnings: If present, warnings are disabled (useful for external projects)
-# {option} [in] low_warnings: If present, low/normal warnings are enabled
-function(setup_gcc target)
- split_args(ignore "OPTIONS" options ${ARGN})
- has_item(option_static_runtime "static_runtime" ${options})
- has_item(option_c "c" ${options})
- has_item(option_cxx "cxx" ${options})
- has_item(option_no_warnings "no_warnings" ${options})
- has_item(option_low_warnings "low_warnings" ${options})
-
- # generates complete debugging information
- target_add_compiler_flag(${target} "-g3" DEBUG RELWITHDEBINFO)
-
- # set optimization
- target_add_compiler_flag(${target} "-O0" DEBUG)
- target_add_compiler_flag(${target} "-O2" RELWITHDEBINFO)
- target_add_compiler_flag(${target} "-O3" RELEASE)
-
- # statically link C runtime library to static_runtime targets
- if(option_static_runtime)
- target_add_compiler_flag(${target} "-static-libgcc")
- target_add_compiler_flag(${target} "-static-libstdc++")
- endif()
-
- # enable sanitizers
- #target_add_linker_flag(${target} "-fsanitize=address" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=thread" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=memory" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=undefined" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=leak" DEBUG RELWITHDEBINFO)
-
- # enable libstdc++ "debug" mode
- # warning: changes the size of some standard class templates
- # you cannot pass containers between translation units compiled
- # with and without libstdc++ "debug" mode
- #target_add_compile_definition(${target} _GLIBCXX_DEBUG DEBUG)
- #target_add_compile_definition(${target} _GLIBCXX_DEBUG_PEDANTIC DEBUG)
-
- # manage warnings
- set(flags)
- set(c_flags)
- set(cxx_flags)
- if(option_no_warnings)
- set(flags "--no-warnings")
- elseif(option_low_warnings)
- set(flags "-pedantic" "-Wall")
- else()
- set(flags
- ## Base flags:
- "-pedantic"
- "-pedantic-errors"
- "-Wall"
- "-Wextra"
-
- ## Extra flags:
- "-Wdouble-promotion"
- "-Wnull-dereference"
- "-Wimplicit-fallthrough"
- "-Wif-not-aligned"
- "-Wmissing-include-dirs"
- "-Wswitch-bool"
- "-Wswitch-unreachable"
- "-Walloc-zero"
- "-Wduplicated-branches"
- "-Wduplicated-cond"
- "-Wfloat-equal"
- "-Wshadow"
- "-Wundef"
- "-Wexpansion-to-defined"
- #"-Wunused-macros"
- "-Wcast-qual"
- "-Wcast-align"
- "-Wwrite-strings"
- "-Wconversion"
- "-Wsign-conversion"
- "-Wdate-time"
- "-Wextra-semi"
- "-Wlogical-op"
- "-Wmissing-declarations"
- "-Wredundant-decls"
- "-Wrestrict"
- #"-Winline"
- "-Winvalid-pch"
- "-Woverlength-strings"
- "-Wformat=2"
- "-Wformat-signedness"
- "-Winit-self"
-
- ## Optimisation dependant flags
- "-Wstrict-overflow=5"
-
- ## Info flags
- #"-Winvalid-pch"
- #"-Wvolatile-register-var"
- #"-Wdisabled-optimization"
- #"-Woverlength-strings"
- #"-Wunsuffixed-float-constants"
- #"-Wvector-operation-performance"
-
- ## Apocalypse flags:
- #"-Wsystem-headers"
- #"-Werror"
-
- ## Exit on first error
- "-Wfatal-errors"
- )
- if(option_c)
- set(c_flags
- "-Wdeclaration-after-statement"
- "-Wbad-function-cast"
- "-Wjump-misses-init"
- "-Wstrict-prototypes"
- "-Wold-style-definition"
- "-Wmissing-prototypes"
- "-Woverride-init-side-effects"
- "-Wnested-externs"
- #"-Wc90-c99-compat"
- #"-Wc99-c11-compat"
- #"-Wc++-compat"
- )
- endif()
- if(option_cxx)
- set(cxx_flags
- "-Wzero-as-null-pointer-constant"
- "-Wsubobject-linkage"
- "-Wdelete-incomplete"
- "-Wuseless-cast"
- "-Wctor-dtor-privacy"
- "-Wnoexcept"
- "-Wregister"
- "-Wstrict-null-sentinel"
- "-Wold-style-cast"
- "-Woverloaded-virtual"
-
- ## Lifetime
- "-Wlifetime"
-
- ## Suggestions
- "-Wsuggest-override"
- #"-Wsuggest-final-types"
- #"-Wsuggest-final-methods"
- #"-Wsuggest-attribute=pure"
- #"-Wsuggest-attribute=const"
- #"-Wsuggest-attribute=noreturn"
- #"-Wsuggest-attribute=format"
-
- ## Guidelines from Scott Meyers’ Effective C++ series of books
- #"-Weffc++"
-
- ## Special purpose
- #"-Wsign-promo"
- #"-Wtemplates"
- #"-Wmultiple-inheritance"
- #"-Wvirtual-inheritance"
- #"-Wnamespaces"
-
- ## Standard versions
- #"-Wc++11-compat"
- #"-Wc++14-compat"
- #"-Wc++17-compat"
- )
- endif()
- endif()
- foreach(flag IN ITEMS ${flags} ${c_flags} ${cxx_flags})
- target_add_compiler_flag(${target} ${flag})
- endforeach()
-endfunction()
-
-## setup_clang(target [OPTIONS [static_runtime] [no_warnings] [low_warnings]])
-# Set up clang-specific options of the target.
-# Without options: maximum warnings are enabled.
-# {value} [in] target: Target to configure
-# {option} [in] static_runtime: If present, C runtime library is statically linked
-# {option} [in] no_warnings: If present, warnings are disabled (useful for external projects)
-# {option} [in] low_warnings: If present, low/normal warnings are enabled
-function(setup_clang target)
- split_args(ignore "OPTIONS" options ${ARGN})
- has_item(option_static_runtime "static_runtime" ${options})
- has_item(option_no_warnings "no_warnings" ${options})
- has_item(option_low_warnings "low_warnings" ${options})
-
- # generates complete debugging information
- target_add_compiler_flag(${target} "-g3" DEBUG RELWITHDEBINFO)
-
- # set optimization
- target_add_compiler_flag(${target} "-O0" DEBUG)
- target_add_compiler_flag(${target} "-O2" RELWITHDEBINFO)
- target_add_compiler_flag(${target} "-O3" RELEASE)
-
- # statically link C runtime library to static_runtime targets
- if(option_static_runtime)
- target_add_compiler_flag(${target} "-static-libgcc")
- target_add_compiler_flag(${target} "-static-libstdc++")
- endif()
-
- # enable sanitizers
- #target_add_linker_flag(${target} "-fsanitize=address" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=thread" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=memory" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=undefined" DEBUG RELWITHDEBINFO)
- #target_add_linker_flag(${target} "-fsanitize=leak" DEBUG RELWITHDEBINFO)
-
- # enable libstdc++ "debug" mode
- # warning: changes the size of some standard class templates
- # you cannot pass containers between translation units compiled
- # with and without libstdc++ "debug" mode
- #target_add_compile_definition(${target} _GLIBCXX_DEBUG DEBUG)
- #target_add_compile_definition(${target} _GLIBCXX_DEBUG_PEDANTIC DEBUG)
-
- # manage warnings
- set(flags)
- if(option_no_warnings)
- set(flags "-Wno-everything")
- elseif(option_low_warnings)
- set(flags "-pedantic" "-Wall")
- else()
- set(flags
- ## Base flags:
- "-pedantic"
- "-pedantic-errors"
- "-Wall"
- "-Wextra"
-
- ## Extra flags:
- "-Wbad-function-cast"
- "-Wcomplex-component-init"
- "-Wconditional-uninitialized"
- "-Wcovered-switch-default"
- "-Wcstring-format-directive"
- "-Wdelete-non-virtual-dtor"
- "-Wdeprecated"
- "-Wdollar-in-identifier-extension"
- "-Wdouble-promotion"
- "-Wduplicate-enum"
- "-Wduplicate-method-arg"
- "-Wembedded-directive"
- "-Wexpansion-to-defined"
- "-Wextended-offsetof"
- "-Wfloat-conversion"
- "-Wfloat-equal"
- "-Wfor-loop-analysis"
- "-Wformat-pedantic"
- "-Wgnu"
- "-Wimplicit-fallthrough"
- "-Winfinite-recursion"
- "-Winvalid-or-nonexistent-directory"
- "-Wkeyword-macro"
- "-Wmain"
- "-Wmethod-signatures"
- "-Wmicrosoft"
- "-Wmismatched-tags"
- "-Wmissing-field-initializers"
- "-Wmissing-method-return-type"
- "-Wmissing-prototypes"
- "-Wmissing-variable-declarations"
- "-Wnested-anon-types"
- "-Wnon-virtual-dtor"
- "-Wnonportable-system-include-path"
- "-Wnull-pointer-arithmetic"
- "-Wnullability-extension"
- "-Wold-style-cast"
- "-Woverriding-method-mismatch"
- "-Wpacked"
- "-Wpedantic"
- "-Wpessimizing-move"
- "-Wredundant-move"
- "-Wreserved-id-macro"
- "-Wself-assign"
- "-Wself-move"
- "-Wsemicolon-before-method-body"
- "-Wshadow"
- "-Wshadow-field"
- "-Wshadow-field-in-constructor"
- "-Wshadow-uncaptured-local"
- "-Wshift-sign-overflow"
- "-Wshorten-64-to-32"
- #"-Wsign-compare"
- #"-Wsign-conversion"
- "-Wsigned-enum-bitfield"
- "-Wstatic-in-inline"
- #"-Wstrict-prototypes"
- #"-Wstring-conversion"
- #"-Wswitch-enum"
- "-Wtautological-compare"
- "-Wtautological-overlap-compare"
- "-Wthread-safety"
- "-Wundefined-reinterpret-cast"
- "-Wuninitialized"
- #"-Wunknown-pragmas"
- "-Wunreachable-code"
- "-Wunreachable-code-aggressive"
- #"-Wunused"
- "-Wunused-const-variable"
- "-Wunused-lambda-capture"
- "-Wunused-local-typedef"
- "-Wunused-parameter"
- "-Wunused-private-field"
- "-Wunused-template"
- "-Wunused-variable"
- "-Wused-but-marked-unused"
- "-Wzero-as-null-pointer-constant"
- "-Wzero-length-array"
-
- ## Lifetime
- "-Wlifetime"
-
- ## Info flags
- "-Wcomma"
- "-Wcomment"
-
- ## Exit on first error
- "-Wfatal-errors"
- )
- endif()
- foreach(flag ${flags})
- target_add_compiler_flag(${target} ${flag})
- endforeach()
-endfunction()
-
-## setup_target(target [OPTIONS [options...]])
-# Set up options of the target depending of the compiler.
-# To know possible option, see the setup_ functions documentation.
-# Currently supported compilers: msvc, gcc.
-# {value} [in] target: Target to configure
-# {option} [in] options: Configuration options
-function(setup_target target)
- if(MSVC)
- setup_msvc(${target} OPTIONS ${ARGN})
- elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
- setup_gcc(${target} OPTIONS ${ARGN})
- elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
- setup_clang(${target} OPTIONS ${ARGN})
- else()
- message(WARNING "Unsupported compiler (${CMAKE_CXX_COMPILER_ID}) setup")
- endif()
-endfunction()
-
-## make_target(target group files... [INCLUDES includes...] [EXT_INCLUDES ext_includes...]
-## [OPTIONS [executable] [test] [shared] [static_runtime] [c] [cxx] [no_warnings] [low_warnings]])
-# Make a new target with the input options
-# By default targets are static libraries.
-# {value} [in] target: Target name
-# {value} [in] group: Group of the target (can contain '/'' for subgroups)
-# {value} [in] files: Source files
-# {value} [in] includes: Include files
-# {value} [in] ext_includes: External include files (no warnings)
-# {option} [in] executable: If present, build an executable
-# {option} [in] test: If present, build a test executable
-# {option} [in] shared: If present, build a shared library
-# {option} [in] static_runtime: If present, C runtime library is statically linked
-# {option} [in] c: If present, the target is written in C, add warnings if no warnings option is specified
-# {option} [in] cxx: If present, the target is written in C++, add warnings if no warnings option is specified
-# {option} [in] no_warnings: If present, warnings are disabled (useful for external projects)
-# {option} [in] low_warnings: If present, low/normal warnings are enabled
-# {option} [in] interface If present, defines the target as an interface
-function(make_target target group)
- message(STATUS "Configuring ${group}/${target}")
-
- # initialize additional include directory list
- set(includes)
- set(ext_includes)
-
- # get options
- split_args(inputs "OPTIONS" options ${ARGN})
- split_args(inputs2 "EXT_INCLUDES" ext_includes ${inputs})
- split_args(files "INCLUDES" includes ${inputs2})
- has_item(is_executable "executable" ${options})
- has_item(is_test "test" ${options})
- has_item(is_shared "shared" ${options})
- has_item(is_interface "interface" ${options})
-
- # sort files
- if (files)
- list(SORT files)
- endif()
-
- # add the target
- if(is_executable OR is_test)
- add_executable(${target} "" ${files})
- if(is_test)
- add_test(NAME ${target} COMMAND ${target})
- #add_custom_command(TARGET ${target} POST_BUILD COMMAND $)
- endif()
- elseif(is_interface)
- add_library(${target} INTERFACE)
- elseif(is_shared)
- add_library(${target} SHARED ${files})
- else()
- add_library(${target} STATIC ${files})
- endif()
-
- # setup compiler dependent options
- if (NOT is_interface)
- setup_target(${target} ${options})
- endif()
-
- # add all additional include directories
- if(NOT is_interface)
- target_add_includes(${target} ${includes})
- target_add_system_includes(${target} ${ext_includes})
- else()
- target_include_directories(${target} INTERFACE ${includes})
- endif()
-
- # set directories for IDE
- source_group(CMake REGULAR_EXPRESSION ".*[.](cmake|rule)$")
- source_group(CMake FILES "CMakeLists.txt")
- if (NOT is_interface)
- set_target_properties(${target} PROPERTIES FOLDER ${group})
- endif()
-endfunction()
-
-## configure_folder(input_folder output_folder [args...])
-# Recursively copy all files from an input folder to an output folder
-# Copy is made with CMake configure_file(), see documentation for more information:
-# https://cmake.org/cmake/help/latest/command/configure_file.html
-# {value} [in] input_folder: Input folder
-# {value} [in] output_folder: Output folder
-# {value} [in] args: CMake configure_file() additional arguments
-function(configure_folder input_folder output_folder)
- file(GLOB_RECURSE files "${input_folder}/*")
- foreach(file ${files})
- file(RELATIVE_PATH relative_file ${input_folder} ${file})
- configure_file(${file} "${output_folder}/${relative_file}" ${ARGN})
- endforeach()
-endfunction()
-
-
-# disable compiler specific extensions
-set(CMAKE_C_EXTENSIONS OFF)
-set(CMAKE_CXX_EXTENSIONS OFF)
-
-# set C_STANDARD/CXX_STANDARD as a requirement
-set(CMAKE_C_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-# don't allow to build in sources otherwise a makefile not generated by CMake can be overridden
-set(CMAKE_DISABLE_SOURCE_CHANGES ON)
-set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
-
-# place generated binaries in build/bin
-file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/bin)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/bin/)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/build/bin/)
-set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/bin/)
-
-# place generated libs in build/lib
-file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build/lib)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/lib/)
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/build/lib/)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/build/lib/)
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/build/lib/)
-set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/lib/)
-set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/build/lib/)
-
-# enable IDE folders
-set_property(GLOBAL PROPERTY USE_FOLDERS ON)
-set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "_CMake")
\ No newline at end of file
diff --git a/cmake/conan.cmake b/cmake/conan.cmake
new file mode 100644
index 0000000..58ed248
--- /dev/null
+++ b/cmake/conan.cmake
@@ -0,0 +1,518 @@
+# The MIT License (MIT)
+
+# Copyright (c) 2018 JFrog
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+
+# This file comes from: https://github.com/conan-io/cmake-conan. Please refer
+# to this repository for issues and documentation.
+
+# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called.
+# It will take CMake current settings (os, compiler, compiler version, architecture)
+# and translate them to conan settings for installing and retrieving dependencies.
+
+# It is intended to facilitate developers building projects that have conan dependencies,
+# but it is only necessary on the end-user side. It is not necessary to create conan
+# packages, in fact it shouldn't be use for that. Check the project documentation.
+
+
+include(CMakeParseArguments)
+
+function(_get_msvc_ide_version result)
+ set(${result} "" PARENT_SCOPE)
+ if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500)
+ set(${result} 8 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600)
+ set(${result} 9 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700)
+ set(${result} 10 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800)
+ set(${result} 11 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900)
+ set(${result} 12 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910)
+ set(${result} 14 PARENT_SCOPE)
+ elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920)
+ set(${result} 15 PARENT_SCOPE)
+ else()
+ message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]")
+ endif()
+endfunction()
+
+function(conan_cmake_settings result)
+ #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER})
+ #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID})
+ #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION})
+ #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS})
+ #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE})
+ #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE})
+ #message(STATUS "GENERATOR " ${CMAKE_GENERATOR})
+ #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64})
+
+ message(STATUS "Conan: Automatic detection of conan settings from cmake")
+
+ parse_arguments(${ARGV})
+
+ if(ARGUMENTS_BUILD_TYPE)
+ set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE})
+ elseif(CMAKE_BUILD_TYPE)
+ set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE})
+ else()
+ message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)")
+ endif()
+ if(ARGUMENTS_ARCH)
+ set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH})
+ endif()
+ #handle -s os setting
+ if(CMAKE_SYSTEM_NAME)
+ #use default conan os setting if CMAKE_SYSTEM_NAME is not defined
+ set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME})
+ if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+ set(CONAN_SYSTEM_NAME Macos)
+ endif()
+ set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore)
+ list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index)
+ if (${_index} GREATER -1)
+ #check if the cmake system is a conan supported one
+ set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME})
+ else()
+ message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}")
+ endif()
+ endif()
+
+ get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+ if (";${_languages};" MATCHES ";CXX;")
+ set(LANGUAGE CXX)
+ set(USING_CXX 1)
+ elseif (";${_languages};" MATCHES ";C;")
+ set(LANGUAGE C)
+ set(USING_CXX 0)
+ else ()
+ message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.")
+ endif()
+
+ if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU)
+ # using GCC
+ # TODO: Handle other params
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ set(COMPILER_VERSION ${MAJOR}.${MINOR})
+ if(${MAJOR} GREATER 4)
+ set(COMPILER_VERSION ${MAJOR})
+ endif()
+ set(_CONAN_SETTING_COMPILER gcc)
+ set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION})
+ if (USING_CXX)
+ conan_cmake_detect_gnu_libcxx(_LIBCXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+ endif ()
+ elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang)
+ # using AppleClang
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ set(_CONAN_SETTING_COMPILER apple-clang)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ if (USING_CXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX libc++)
+ endif ()
+ elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang)
+ string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+ list(GET VERSION_LIST 0 MAJOR)
+ list(GET VERSION_LIST 1 MINOR)
+ if(APPLE)
+ cmake_policy(GET CMP0025 APPLE_CLANG_POLICY_ENABLED)
+ if(NOT APPLE_CLANG_POLICY_ENABLED)
+ message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it")
+ set(_CONAN_SETTING_COMPILER apple-clang)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ else()
+ set(_CONAN_SETTING_COMPILER clang)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ endif()
+ if (USING_CXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX libc++)
+ endif ()
+ else()
+ set(_CONAN_SETTING_COMPILER clang)
+ if(${MAJOR} GREATER 7)
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR})
+ else()
+ set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+ endif()
+ if (USING_CXX)
+ conan_cmake_detect_gnu_libcxx(_LIBCXX)
+ set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+ endif ()
+ endif()
+ elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC)
+ set(_VISUAL "Visual Studio")
+ _get_msvc_ide_version(_VISUAL_VERSION)
+ if("${_VISUAL_VERSION}" STREQUAL "")
+ message(FATAL_ERROR "Conan: Visual Studio not recognized")
+ else()
+ set(_CONAN_SETTING_COMPILER ${_VISUAL})
+ set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION})
+ endif()
+
+ if(NOT _CONAN_SETTING_ARCH)
+ if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64")
+ set(_CONAN_SETTING_ARCH x86_64)
+ elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM")
+ message(STATUS "Conan: Using default ARM architecture from MSVC")
+ set(_CONAN_SETTING_ARCH armv6)
+ elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86")
+ set(_CONAN_SETTING_ARCH x86)
+ else ()
+ message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]")
+ endif()
+ endif()
+
+ conan_cmake_detect_vs_runtime(_vs_runtime)
+ message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}")
+ set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime})
+
+ if (CMAKE_GENERATOR_TOOLSET)
+ set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+ elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja"))
+ set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+ endif()
+ else()
+ message(FATAL_ERROR "Conan: compiler setup not recognized")
+ endif()
+
+ # If profile is defined it is used
+ if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE)
+ set(_SETTINGS -pr ${ARGUMENTS_DEBUG_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE)
+ set(_SETTINGS -pr ${ARGUMENTS_RELEASE_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE)
+ set(_SETTINGS -pr ${ARGUMENTS_RELWITHDEBINFO_PROFILE})
+ elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE)
+ set(_SETTINGS -pr ${ARGUMENTS_MINSIZEREL_PROFILE})
+ elseif(ARGUMENTS_PROFILE)
+ set(_SETTINGS -pr ${ARGUMENTS_PROFILE})
+ endif()
+
+ if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL")
+ set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version
+ compiler.runtime compiler.libcxx compiler.toolset)
+ endif()
+
+ # Automatic from CMake
+ foreach(ARG ${ARGUMENTS_PROFILE_AUTO})
+ string(TOUPPER ${ARG} _arg_name)
+ string(REPLACE "." "_" _arg_name ${_arg_name})
+ if(_CONAN_SETTING_${_arg_name})
+ set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}})
+ endif()
+ endforeach()
+
+ foreach(ARG ${ARGUMENTS_SETTINGS})
+ set(_SETTINGS ${_SETTINGS} -s ${ARG})
+ endforeach()
+
+ message(STATUS "Conan: Settings= ${_SETTINGS}")
+
+ set(${result} ${_SETTINGS} PARENT_SCOPE)
+endfunction()
+
+
+function(conan_cmake_detect_gnu_libcxx result)
+ # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake
+ if(DEFINED _GLIBCXX_USE_CXX11_ABI)
+ if(_GLIBCXX_USE_CXX11_ABI)
+ set(${result} libstdc++11 PARENT_SCOPE)
+ return()
+ else()
+ set(${result} libstdc++ PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ # Check if there's any add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+ get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS)
+ foreach(define ${defines})
+ if(define STREQUAL "_GLIBCXX_USE_CXX11_ABI=0")
+ set(${result} libstdc++ PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+
+ # Use C++11 stdlib as default if gcc is 5.1+
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.1")
+ set(${result} libstdc++ PARENT_SCOPE)
+ else()
+ set(${result} libstdc++11 PARENT_SCOPE)
+ endif()
+endfunction()
+
+
+function(conan_cmake_detect_vs_runtime result)
+ string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)
+ set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS)
+ foreach(variable ${variables})
+ string(REPLACE " " ";" flags ${${variable}})
+ foreach (flag ${flags})
+ if(${flag} STREQUAL "/MD" OR ${flag} STREQUAL "/MDd" OR ${flag} STREQUAL "/MT" OR ${flag} STREQUAL "/MTd")
+ string(SUBSTRING ${flag} 1 -1 runtime)
+ set(${result} ${runtime} PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+ endforeach()
+ if(${build_type} STREQUAL "DEBUG")
+ set(${result} "MDd" PARENT_SCOPE)
+ else()
+ set(${result} "MD" PARENT_SCOPE)
+ endif()
+endfunction()
+
+
+macro(parse_arguments)
+ set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_OUTPUT_DIRS OUTPUT_QUIET NO_IMPORTS)
+ set(oneValueArgs CONANFILE DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE
+ PROFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND)
+ set(multiValueArgs REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO INSTALL_ARGS)
+ cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+endmacro()
+
+function(conan_cmake_install)
+ # Calls "conan install"
+ # Argument BUILD is equivalant to --build={missing, PkgName,...} or
+ # --build when argument is 'BUILD all' (which builds all packages from source)
+ # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source
+ # cmake does not identify conan as command, even if it is +x and it is in the path
+ parse_arguments(${ARGV})
+
+ if(CONAN_CMAKE_MULTI)
+ set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi)
+ else()
+ set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake)
+ endif()
+
+ set(CONAN_BUILD_POLICY "")
+ foreach(ARG ${ARGUMENTS_BUILD})
+ if(${ARG} STREQUAL "all")
+ set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build)
+ break()
+ else()
+ set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG})
+ endif()
+ endforeach()
+ if(ARGUMENTS_CONAN_COMMAND)
+ set(conan_command ${ARGUMENTS_CONAN_COMMAND})
+ else()
+ set(conan_command conan)
+ endif()
+ set(CONAN_OPTIONS "")
+ if(ARGUMENTS_CONANFILE)
+ set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE})
+ # A conan file has been specified - apply specified options as well if provided
+ foreach(ARG ${ARGUMENTS_OPTIONS})
+ set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG})
+ endforeach()
+ else()
+ set(CONANFILE ".")
+ endif()
+ if(ARGUMENTS_UPDATE)
+ set(CONAN_INSTALL_UPDATE --update)
+ endif()
+ if(ARGUMENTS_NO_IMPORTS)
+ set(CONAN_INSTALL_NO_IMPORTS --no-imports)
+ endif()
+ set(CONAN_INSTALL_FOLDER "")
+ if(ARGUMENTS_INSTALL_FOLDER)
+ set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER})
+ endif()
+ foreach(ARG ${ARGUMENTS_GENERATORS})
+ set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG})
+ endforeach()
+ foreach(ARG ${ARGUMENTS_ENV})
+ set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG})
+ endforeach()
+ set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_INSTALL_NO_IMPORTS} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER} ${ARGUMENTS_INSTALL_ARGS})
+
+ string (REPLACE ";" " " _conan_args "${conan_args}")
+ message(STATUS "Conan executing: ${conan_command} ${_conan_args}")
+
+ if(ARGUMENTS_OUTPUT_QUIET)
+ set(OUTPUT_CONTROL OUTPUT_QUIET)
+ endif()
+
+ execute_process(COMMAND ${conan_command} ${conan_args}
+ RESULT_VARIABLE return_code
+ OUTPUT_VARIABLE conan_output
+ ERROR_VARIABLE conan_output
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+ message(STATUS "${conan_output}")
+
+ if(NOT "${return_code}" STREQUAL "0")
+ message(FATAL_ERROR "Conan install failed='${return_code}'")
+ endif()
+
+endfunction()
+
+
+function(conan_cmake_setup_conanfile)
+ parse_arguments(${ARGV})
+ if(ARGUMENTS_CONANFILE)
+ # configure_file will make sure cmake re-runs when conanfile is updated
+ configure_file(${ARGUMENTS_CONANFILE} ${ARGUMENTS_CONANFILE}.junk)
+ file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${ARGUMENTS_CONANFILE}.junk)
+ else()
+ conan_cmake_generate_conanfile(${ARGV})
+ endif()
+endfunction()
+
+function(conan_cmake_generate_conanfile)
+ # Generate, writing in disk a conanfile.txt with the requires, options, and imports
+ # specified as arguments
+ # This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR)
+ parse_arguments(${ARGV})
+ set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt")
+
+ file(WRITE ${_FN} "[generators]\ncmake\n\n[requires]\n")
+ foreach(ARG ${ARGUMENTS_REQUIRES})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+
+ file(APPEND ${_FN} ${ARG} "\n[options]\n")
+ foreach(ARG ${ARGUMENTS_OPTIONS})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+
+ file(APPEND ${_FN} ${ARG} "\n[imports]\n")
+ foreach(ARG ${ARGUMENTS_IMPORTS})
+ file(APPEND ${_FN} ${ARG} "\n")
+ endforeach()
+endfunction()
+
+
+macro(conan_load_buildinfo)
+ if(CONAN_CMAKE_MULTI)
+ set(_CONANBUILDINFO conanbuildinfo_multi.cmake)
+ else()
+ set(_CONANBUILDINFO conanbuildinfo.cmake)
+ endif()
+ # Checks for the existence of conanbuildinfo.cmake, and loads it
+ # important that it is macro, so variables defined at parent scope
+ if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO}")
+ message(STATUS "Conan: Loading ${_CONANBUILDINFO}")
+ include(${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO})
+ else()
+ message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+endmacro()
+
+
+macro(conan_cmake_run)
+ parse_arguments(${ARGV})
+
+ if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED
+ AND NOT ARGUMENTS_BUILD_TYPE)
+ set(CONAN_CMAKE_MULTI ON)
+ message(STATUS "Conan: Using cmake-multi generator")
+ else()
+ set(CONAN_CMAKE_MULTI OFF)
+ endif()
+
+ if(NOT CONAN_EXPORTED)
+ conan_cmake_setup_conanfile(${ARGV})
+ if(CONAN_CMAKE_MULTI)
+ foreach(CMAKE_BUILD_TYPE "Release" "Debug")
+ set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE})
+ conan_cmake_settings(settings ${ARGV})
+ conan_cmake_install(SETTINGS ${settings} ${ARGV})
+ endforeach()
+ set(CMAKE_BUILD_TYPE)
+ else()
+ conan_cmake_settings(settings ${ARGV})
+ conan_cmake_install(SETTINGS ${settings} ${ARGV})
+ endif()
+ endif()
+
+ conan_load_buildinfo()
+
+ if(ARGUMENTS_BASIC_SETUP)
+ foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS)
+ if(ARGUMENTS_${_option})
+ if(${_option} STREQUAL "CMAKE_TARGETS")
+ list(APPEND _setup_options "TARGETS")
+ else()
+ list(APPEND _setup_options ${_option})
+ endif()
+ endif()
+ endforeach()
+ conan_basic_setup(${_setup_options})
+ endif()
+endmacro()
+
+macro(conan_check)
+ # Checks conan availability in PATH
+ # Arguments REQUIRED and VERSION are optional
+ # Example usage:
+ # conan_check(VERSION 1.0.0 REQUIRED)
+ message(STATUS "Conan: checking conan executable in path")
+ set(options REQUIRED)
+ set(oneValueArgs VERSION)
+ cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN})
+
+ find_program(CONAN_CMD conan)
+ if(NOT CONAN_CMD AND CONAN_REQUIRED)
+ message(FATAL_ERROR "Conan executable not found!")
+ endif()
+ message(STATUS "Conan: Found program ${CONAN_CMD}")
+ execute_process(COMMAND ${CONAN_CMD} --version
+ OUTPUT_VARIABLE CONAN_VERSION_OUTPUT
+ ERROR_VARIABLE CONAN_VERSION_OUTPUT)
+ message(STATUS "Conan: Version found ${CONAN_VERSION_OUTPUT}")
+
+ if(DEFINED CONAN_VERSION)
+ string(REGEX MATCH ".*Conan version ([0-9]+\.[0-9]+\.[0-9]+)" FOO
+ "${CONAN_VERSION_OUTPUT}")
+ if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION})
+ message(FATAL_ERROR "Conan outdated. Installed: ${CONAN_VERSION}, \
+ required: ${CONAN_VERSION_REQUIRED}. Consider updating via 'pip \
+ install conan --upgrade'.")
+ endif()
+ endif()
+endmacro()
+
+macro(conan_add_remote)
+ # Adds a remote
+ # Arguments URL and NAME are required, INDEX is optional
+ # Example usage:
+ # conan_add_remote(NAME bincrafters INDEX 1
+ # URL https://api.bintray.com/conan/bincrafters/public-conan)
+ set(oneValueArgs URL NAME INDEX)
+ cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN})
+
+ if(DEFINED CONAN_INDEX)
+ set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}")
+ endif()
+
+ message(STATUS "Conan: Adding ${CONAN_NAME} remote repositoy (${CONAN_URL})")
+ execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_URL}
+ ${CONAN_INDEX_ARG} -f)
+endmacro()
diff --git a/cmake/ctulu.cmake b/cmake/ctulu.cmake
new file mode 100644
index 0000000..ba2c14c
--- /dev/null
+++ b/cmake/ctulu.cmake
@@ -0,0 +1,8 @@
+list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR})
+
+include(ctulu_private)
+include(ctulu_target)
+include(ctulu_utils)
+include(ctulu_clang-format)
+include(ctulu_warnings)
+include(ctulu_setup_cmake)
diff --git a/cmake/ctulu_clang-format.cmake b/cmake/ctulu_clang-format.cmake
new file mode 100644
index 0000000..8be0c64
--- /dev/null
+++ b/cmake/ctulu_clang-format.cmake
@@ -0,0 +1,70 @@
+## target_generate_clang_format(target_name clang_format_target_name [FILES files...] [DIRS [NORECURSE] directories...])
+# Generate a format target (${target_name}) which format the given files
+# The generated target lanch clang-format on all the given files with -style=file
+# {value} [in] target_name: Name of the format target
+# {value} [in] clang_format_target_name: Name of the generated target
+# {value} [in] files: Sources files
+# {value} [in] directories: Directories from which sources files are generated
+function(ctulu_generate_clang_format clang_format_target_name)
+ find_program(CLANG_FORMAT clang-format
+ NAMES clang-format-9 clang-format-8 clang-format-7 clang-format-6)
+
+ set(options NORECURSE)
+ set(multiValueArgs DIRS FILES)
+ cmake_parse_arguments(ctulu_generate_clang_format "${options}" "" "${multiValueArgs}" ${ARGN})
+
+ if(${CLANG_FORMAT} STREQUAL CLANG_FORMAT-NOTFOUND)
+ message(WARNING "Ctulu -- clang-format not found, ${format-target} not generated")
+ return()
+ else()
+ message(STATUS "Ctulu -- clang-format found: ${CLANG_FORMAT}")
+ endif()
+
+ if(ctulu_generate_clang_format_DIRS)
+ foreach(it ${ctulu_generate_clang_format_DIRS})
+ if(ctulu_generate_clang_format_NORECURSE)
+ ctulu_list_files(tmp_files ${it} NORECURSE)
+ else()
+ ctulu_list_files(tmp_files ${it})
+ endif()
+ set(ctulu_generate_clang_format_FILES ${ctulu_generate_clang_format_FILES} ${tmp_files})
+ endforeach()
+ endif()
+
+ add_custom_target(
+ ${clang_format_target_name}
+ COMMAND "${CLANG_FORMAT}" -style=file -i ${ctulu_generate_clang_format_FILES}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ VERBATIM)
+
+ message(STATUS "Ctulu -- Format target ${clang_format_target_name} generated")
+endfunction()
+
+## target_generate_clang_format(target)
+# Generate a format target named ${clang_format_target_name} for the target (${target_name}).
+# The generated target lanch clang-format on all the target sources with -style=file
+# {value} [in] target: Target from wich generate format target
+# {value} [in] clang_format_target_name: Name of the generated target
+function(ctulu_generate_clang_format_from_target target_name clang_format_target_name)
+ if(NOT TARGET ${target_name})
+ message(FATAL_ERROR "Ctulu -- ${target_name} is not a target)")
+ endif()
+
+ find_program(CLANG_FORMAT clang-format
+ NAMES clang-format-9 clang-format-8 clang-format-7 clang-format-6)
+ if(${CLANG_FORMAT} STREQUAL CLANG_FORMAT-NOTFOUND)
+ message(WARNING "Ctulu -- clang-format not found, ${clang_format_target_name} not generated")
+ return()
+ else()
+ message(STATUS "Ctulu -- clang-format found: ${CLANG_FORMAT}")
+ endif()
+
+ get_target_property(target_sources ${target_name} SOURCES)
+ add_custom_target(
+ ${clang_format_target_name}
+ COMMAND "${CLANG_FORMAT}" -style=file -i ${target_sources}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ VERBATIM)
+
+ message(STATUS "Ctulu -- Format target \"${clang_format_target_name}\" generated")
+endfunction()
\ No newline at end of file
diff --git a/cmake/ctulu_private.cmake b/cmake/ctulu_private.cmake
new file mode 100644
index 0000000..fa000fc
--- /dev/null
+++ b/cmake/ctulu_private.cmake
@@ -0,0 +1,160 @@
+########################################
+## Private function of ctulu library. ##
+########################################
+
+#ctulu_check_options_coherence(target_name group_name
+function(ctulu_check_options_coherence target_name group_name)
+ set(options EXECUTABLE TEST SHARED STATIC INTERFACE)
+ set(oneValueArgs C CXX W_LEVEL)
+ set(multiValueArgs FILES DIRS PRIVATE_INCLUDES PUBLIC_INCLUDES INTERFACE_INCLUDES EXT_INCLUDES)
+ cmake_parse_arguments(ctulu_check_options_coherence "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(target_types_options)
+ foreach(it ${options})
+ set(insert_list target_types_options)
+ if("${it}" STREQUAL "NO_WARNINGS" OR "${it}" STREQUAL "LOW_WARNINGS")
+ set(insert_list warning_levels)
+ endif()
+ if(${ctulu_check_options_coherence_${it}})
+ list(APPEND ${insert_list} ${it})
+ endif()
+ endforeach()
+
+ set(options_list target_types_options warning_levels)
+
+ foreach(it ${options_list})
+ list(LENGTH ${it} len)
+ if(${len} GREATER 1)
+ string(REPLACE ";" "," print_list "${${it}}")
+ message(FATAL_ERROR "Ctulu -- When creating ${group_name}/${target_name}: \"${print_list}\" can't be activated at the same time.")
+ endif()
+ endforeach()
+
+ if(ctulu_check_options_coherence_HEADER_ONLY)
+ list(LENGTH ctulu_check_options_coherence_PRIVATE_INCLUDES len)
+ if(${len} GREATER 0)
+ message(FATAL_ERROR "Ctulu -- When creating ${group_name}/${target_name}: Header only libraries can't have private includes")
+ endif()
+ endif()
+endfunction()
+
+#ctulu_list_files(output dir [NORECURSE])
+function(ctulu_list_files output dir)
+ set(options NORECURSE)
+ set(oneValueArgs)
+ set(multiValueArgs)
+ cmake_parse_arguments(ctulu_list_files "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(glob GLOB_RECURSE)
+ if(ctulu_list_files_NORECURSE)
+ set(glob GLOB)
+ endif()
+
+ set(patterns
+ "${dir}/*.c"
+ "${dir}/*.C"
+ "${dir}/*.c++"
+ "${dir}/*.cc"
+ "${dir}/*.cpp"
+ "${dir}/*.cxx"
+ "${dir}/*.h"
+ "${dir}/*.hh"
+ "${dir}/*.h++"
+ "${dir}/*.hpp"
+ "${dir}/*.hxx"
+ "${dir}/*.txx"
+ "${dir}/*.tpp")
+
+ file(${glob} tmp_files ${patterns})
+ set(${output} ${tmp_files} PARENT_SCOPE)
+endfunction()
+
+function(ctulu_target_include_wrapper target_name)
+ set(options SYSTEM)
+ set(oneValueArgs)
+ set(multiValueArgs)
+ cmake_parse_arguments(ctulu_target_include_wrapper "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(ctulu_target_include_wrapper_SYSTEM)
+ ctulu_target_include_directories_impl(${target_name} ${ctulu_target_include_wrapper_UNPARSED_ARGUMENTS} SYSTEM)
+ else()
+ ctulu_target_include_directories_impl(${target_name} ${ctulu_target_include_wrapper_UNPARSED_ARGUMENTS})
+ endif()
+endfunction()
+
+#ctulu_create_file_architecture(output dirs... [NORECURSE])
+function(ctulu_create_file_architecture output)
+ set(options NORECURSE)
+ set(oneValueArgs)
+ set(multiValueArgs)
+ cmake_parse_arguments(ctulu_create_file_architecture "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(files)
+ foreach(it ${ctulu_create_file_architecture_UNPARSED_ARGUMENTS})
+ if(IS_DIRECTORY ${it})
+ if(ctulu_create_file_architecture_NORECURSE)
+ ctulu_list_files(tmp_files ${it} NORECURSE)
+ else()
+ ctulu_list_files(tmp_files ${it})
+ endif()
+ list(APPEND files ${tmp_files})
+ get_filename_component(parent_dir ${it} DIRECTORY)
+ ctulu_assign_files(Sources "${parent_dir}" ${tmp_files})
+ else()
+ list(APPEND files ${it})
+ get_filename_component(dir ${it} DIRECTORY)
+ ctulu_assign_files(Sources "${dir}" ${it})
+ endif()
+ endforeach()
+ set(${output} ${files} PARENT_SCOPE)
+endfunction()
+
+function(ctulu_assign_files group root)
+ foreach(it ${ARGN})
+ get_filename_component(dir ${it} PATH)
+ file(RELATIVE_PATH relative ${root} ${dir})
+ set(local ${group})
+ if(NOT "${relative}" STREQUAL "")
+ set(local "${group}/${relative}")
+ endif()
+ # replace '/' and '\' (and repetitions) by '\\'
+ string(REGEX REPLACE "[\\\\\\/]+" "\\\\\\\\" local ${local})
+ source_group("${local}" FILES ${it})
+ endforeach()
+endfunction()
+
+function(ctulu_target_include_directories_impl target_name)
+ set(options SYSTEM INTERFACE PUBLIC PRIVATE)
+ set(oneValueArgs)
+ set(multiValueArgs)
+ cmake_parse_arguments(ctulu_target_include_directories_impl "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ list(LENGTH ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS size)
+ if(NOT ${size} GREATER 0)
+ return()
+ endif()
+
+ list(REMOVE_DUPLICATES ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS)
+ list(SORT ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS)
+
+ if(ctulu_target_include_directories_impl_INTERFACE)
+ if(ctulu_target_include_directories_impl_SYSTEM)
+ target_include_directories(${target_name} SYSTEM INTERFACE ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ else()
+ target_include_directories(${target_name} INTERFACE ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ endif()
+ elseif(ctulu_target_include_directories_impl_PUBLIC)
+ if(ctulu_target_include_directories_impl_SYSTEM)
+ target_include_directories(${target_name} SYSTEM PUBLIC ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ else()
+ target_include_directories(${target_name} PUBLIC ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ endif()
+ else()
+ if(ctulu_target_include_directories_impl_SYSTEM)
+ target_include_directories(${target_name} SYSTEM PRIVATE ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ else()
+ target_include_directories(${target_name} PRIVATE ${ctulu_target_include_directories_impl_UNPARSED_ARGUMENTS})
+ endif()
+ endif()
+endfunction()
+
diff --git a/cmake/ctulu_setup_cmake.cmake b/cmake/ctulu_setup_cmake.cmake
new file mode 100644
index 0000000..2bdd5dd
--- /dev/null
+++ b/cmake/ctulu_setup_cmake.cmake
@@ -0,0 +1,30 @@
+# ctulu_setup_cmake([DISABLE_SRC_BUILD] [REQUIRE_STANDARD languages...])
+# Configure some variables of CMake.
+# {option} [in] DISABLE_SRC_BUILD: Set ${CMAKE_DISABLE_SOURCE_CHANGES} and ${CMAKE_DISABLE_IN_SOURCE_BUILD} to ON.
+# {option} [in] languages: Disable compiler extension and requires exact standard the given language.
+# (C and CXX supported)
+function(ctulu_setup_cmake)
+ set(options DISABLE_SRC_BUILD)
+ set(oneValueArgs)
+ set(multiValueArgs REQUIRE_STANDARD)
+ cmake_parse_arguments(ctulu_setup_cmake "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if(ctulu_setup_cmake_REQUIRE_STANDARD)
+ message(STATUS "Ctulu -- Disabling Compiler extension")
+ endif()
+
+ foreach(language ${ctulu_setup_cmake_REQUIRE_STANDARD})
+ # disable compiler specific extensions
+ set(CMAKE_${language}_EXTENSIONS OFF PARENT_SCOPE)
+ # set STANDARD as a requirement
+ set(CMAKE_${language}_STANDARD_REQUIRED ON PARENT_SCOPE)
+ endforeach()
+
+ if (ctulu_setup_cmake_DISABLE_SRC_BUILD)
+ message(STATUS "Ctulu -- Disabling build in source")
+
+ # don't allow to build in sources otherwise a makefile not generated by CMake can be overridden
+ set(CMAKE_DISABLE_SOURCE_CHANGES ON PARENT_SCOPE)
+ set(CMAKE_DISABLE_IN_SOURCE_BUILD ON PARENT_SCOPE)
+ endif()
+endfunction()
\ No newline at end of file
diff --git a/cmake/ctulu_target.cmake b/cmake/ctulu_target.cmake
new file mode 100644
index 0000000..764b78a
--- /dev/null
+++ b/cmake/ctulu_target.cmake
@@ -0,0 +1,113 @@
+## ctulu_create_target(target_name group_name [FILES files...] [DIRS [NORECURSE] dirs...]
+## [PRIVATE_INCLUDES [SYSTEM] pr_includes...] [PUBLIC_INCLUDES [SYSTEM] pu_includes...]
+## [INTERFACE_INCLUDES [SYSTEM] in_includes...] [EXT_INCLUDES ext_includes...]
+## [C c_version] [CXX cxx_version]
+## []
+## [W_LEVEL wlevel])
+# Make a new target with the input options
+# By default targets are static libraries.
+# {value} [in] target_name: Name of the target.
+# {value} [in] group_name: Group of the target.
+# {value} [in] files: Files of the target. Please note that if your want the folder in VS
+# you need to use ${dirs}.
+# {value} [in] dirs Add all files in the directory. Use "NORECURSE" if you don't want to include
+# sources from the subdirectories.
+# {value} [in] pr_includes: Private includes. Use system to disable warning for these includes.
+# {value} [in] pu_includes: Public includes. Use system to disable warning for these includes.
+# {value} [in] in_includes: Interface includes. Use system to disable warning for these includes.
+# {value} [in] ext_includes: Externals includes. Shortcut for "PRIVATE_INCLUDES SYSTEM ${ext_includes}"
+# {value} [in] c_version: C Standard to use.
+# {value} [in] cxx_version: C++ Standard to use.
+# {option} [in] target_type: Defines the type of the target. Could be one of
+# {value} [in] wlevel Level of warning to activate. (See ctulu_add_warning_from_file function in ctulu_warnings.cmake)
+function(ctulu_create_target target_name group_name)
+ message(STATUS "Ctulu -- Configuring \"${group_name}/${target_name}\"")
+
+ set(options EXECUTABLE TEST SHARED STATIC INTERFACE)
+ set(oneValueArgs C CXX W_LEVEL)
+ set(multiValueArgs FILES DIRS PRIVATE_INCLUDES PUBLIC_INCLUDES INTERFACE_INCLUDES EXT_INCLUDES)
+ cmake_parse_arguments(ctulu_create_target "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ ctulu_check_options_coherence(${target_name} ${group_name} ${ARGN})
+
+ if(NOT ctulu_create_target_W_LEVEL)
+ set(ctulu_create_target_W_LEVEL "0")
+ endif()
+
+ set(ctulu_${target_name}_warning_level ${ctulu_create_target_W_LEVEL} CACHE INTERNAL "Level of warning used for the target ${target_name}" FORCE)
+
+ if(ctulu_create_target_EXECUTABLE OR ctulu_create_target_TEST)
+ add_executable(${target_name})
+ set(ctulu_target_list_${target_name}_TYPE "EXECUTABLE" PARENT_SCOPE)
+
+ if(ctulu_create_target_TEST)
+ set(ctulu_target_list_${target_name}_TYPE "TEST" PARENT_SCOPE)
+ add_test(NAME ${target_name} COMMAND ${target_name})
+ endif()
+ elseif(ctulu_create_target_INTERFACE)
+ set(ctulu_target_list_${target_name}_TYPE "INTERFACE" PARENT_SCOPE)
+ add_library(${target_name} INTERFACE)
+ elseif(ctulu_create_target_SHARED)
+ set(ctulu_target_list_${target_name}_TYPE "SHARED" PARENT_SCOPE)
+ add_library(${target_name} SHARED)
+ else()
+ set(ctulu_target_list_${target_name}_TYPE "STATIC" PARENT_SCOPE)
+ add_library(${target_name} STATIC)
+ endif()
+
+ if(NOT ctulu_create_target_INTERFACE)
+ ctulu_target_language(${target_name} C ${ctulu_create_target_C} CXX ${ctulu_create_target_CXX})
+ endif()
+
+ if(ctulu_create_target_FILES OR ctulu_create_target_UNPARSED_ARGUMENTS)
+ message(STATUS "Ctulu -- Adding sources")
+ target_sources(${target_name} PRIVATE ${ctulu_create_target_FILES} ${ctulu_create_target_UNPARSED_ARGUMENTS})
+ endif()
+ if(ctulu_create_target_DIRS)
+ message(STATUS "Ctulu -- Adding sources from directories")
+ ctulu_create_file_architecture(sources ${ctulu_create_target_DIRS})
+ target_sources(${target_name} PRIVATE ${sources})
+ endif()
+
+ ctulu_target_include_directories(${target_name}
+ ${ctulu_create_target_EXT_INCLUDES}
+ INTERFACE ${ctulu_create_target_INTERFACE_INCLUDES}
+ PUBLIC ${ctulu_create_target_PUBLIC_INCLUDES}
+ PRIVATE ${ctulu_create_target_PRIVATE_INCLUDES})
+
+ source_group(CMake REGULAR_EXPRESSION ".*[.](cmake|rule)$")
+ source_group(CMake FILES "CMakeLists.txt")
+ if(NOT ctulu_create_target_INTERFACE)
+ set_target_properties(${target_name} PROPERTIES FOLDER ${group_name})
+ else()
+ # Beautiful workaround
+ if(MSVC AND ctulu_create_target_INTERFACE_INCLUDES)
+ ctulu_list_files(tmp_files ${ctulu_create_target_INTERFACE_INCLUDES})
+ add_custom_target("${target_name}.headers" SOURCES ${tmp_files})
+ endif()
+ endif()
+endfunction()
+
+# ctulu_target_compiler_flag(target_name w_flag language configuration)
+# Add the given flag (${w_flag}) to the target (${target_name}) for the given configuration (${configuration})
+# for the given language (${language})
+# {value} [in] target_name: Name of the target
+# {value} [in] w_flag: File where the warning are
+# {value} [in] language: Language of the warning (C or CXX)
+# {value} [in] configuration: Build type
+function(ctulu_target_compiler_flag target_name w_flag language configuration)
+ string(TOUPPER ${language} language)
+ if(language STREQUAL "CXX")
+ check_cxx_compiler_flag(${w_flag} has${w_flag})
+ if(has${w_flag})
+ target_compile_options(${target_name} PRIVATE "$<$,$>:${w_flag}>")
+ endif()
+ elseif(language STREQUAL "C")
+ check_c_compiler_flag(${w_flag} has${w_flag})
+ if(has${w_flag})
+ target_compile_options(${target_name} PRIVATE "$<$,$>:${w_flag}>")
+ endif()
+ else()
+ message(WARNING "Ctulu -- For target \"${target_name}:${configuration}\" language \"${language}\" unknown")
+ endif()
+endfunction()
diff --git a/cmake/ctulu_utils.cmake b/cmake/ctulu_utils.cmake
new file mode 100644
index 0000000..66312df
--- /dev/null
+++ b/cmake/ctulu_utils.cmake
@@ -0,0 +1,89 @@
+## ctulu_target_include_directories(target_name [PRIVATE [SYSTEM] pr_includes...]
+## [PUBLIC [SYSTEM] pu_includes...] [INTERFACE [SYSTEM] in_includes...] [EXT_INCLUDES ext_includes...])
+# Set includes for the given target
+# {value} [in] pr_includes: Private includes. Use system to disable warning for these includes.
+# {value} [in] pu_includes: Public includes. Use system to disable warning for these includes.
+# {value} [in] in_includes: Interface includes. Use system to disable warning for these includes.
+# {value} [in] ext_includes: Externals includes. Shortcut for "PRIVATE_INCLUDES SYSTEM ${ext_includes}"
+function(ctulu_target_include_directories target_name)
+ set(options)
+ set(oneValueArgs)
+ set(multiValueArgs PRIVATE PUBLIC EXT_INCLUDES INTERFACE)
+ cmake_parse_arguments(ctulu_target_include_directories "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ ctulu_target_include_wrapper(${target_name} ${ctulu_target_include_directories_EXT_INCLUDES} SYSTEM)
+ ctulu_target_include_wrapper(${target_name} ${ctulu_target_include_directories_PRIVATE} ${ctulu_target_include_directories_UNPARSED_ARGUMENTS} PRIVATE)
+ ctulu_target_include_wrapper(${target_name} ${ctulu_target_include_directories_PUBLIC} PUBLIC)
+ ctulu_target_include_wrapper(${target_name} ${ctulu_target_include_directories_INTERFACE} INTERFACE)
+
+ if(NOT "${ctulu_target_list_${target_name}_TYPE}" STREQUAL "INTERFACE")
+ ctulu_create_file_architecture(ignored
+ ${ctulu_target_include_directories_EXT_INCLUDES}
+ ${ctulu_target_include_directories_PRIVATE}
+ ${ctulu_target_include_directories_PUBLIC})
+ else()
+ if(MSVC OR XCODE)
+ ctulu_list_files(tmp_files ${ctulu_target_include_directories_INTERFACE})
+ add_custom_target("${target_name}.headers" SOURCES ${tmp_files})
+ endif()
+ endif()
+endfunction()
+
+## ctulu_target_language(target_name [C c_version] [CXX cxx_version])
+## Set language standard for the given target.
+# {value} [in] target_name: Name of the target
+# {value} [in] c_version: C Standard to use.
+# {value} [in] cxx_version: C++ Standard to use.
+function(ctulu_target_language target_name)
+ set(options)
+ set(oneValueArgs C CXX)
+ set(multiValueArgs)
+ cmake_parse_arguments(ctulu_target_language "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ foreach(it ${oneValueArgs})
+ if(ctulu_target_language_${it})
+ message(STATUS "Ctulu -- \"${target_name}\" ${it} standard version: ${ctulu_target_language_${it}}")
+ set_property(TARGET ${target_name} PROPERTY ${it}_STANDARD ${ctulu_target_language_${it}})
+ endif()
+ endforeach()
+endfunction()
+
+## ctulu_target_sources(target_name files... [FILES files...] [DIRS dirs...]
+## [] [NORECURSE])
+# Set sources for the given target
+# {value} [in] files: Files of the target. Please note that if your want the folder in VS
+# you need to use ${dirs}.
+# {value} [in] dirs Add all files in the directory. Use "NORECURSE" if you don't want to include
+# sources from the subdirectories.
+# {option} [in] visibility Set the visibility of the sources. Could be one of
+function(ctulu_target_sources target_name)
+ set(options PRIVATE PUBLIC INTERFACE NORECURSE)
+ set(oneValueArgs)
+ set(multiValueArgs FILES DIRS)
+ cmake_parse_arguments(ctulu_target_sources "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(target_option PRIVATE)
+ foreach(it ${options})
+ if("${it}" STREQUAL "NORECURSE")
+ continue()
+ endif()
+ if(ctulu_target_sources_${it})
+ set(target_option ${it})
+ endif ()
+ endforeach()
+
+ if(ctulu_target_sources_FILES OR ctulu_target_sources_UNPARSED_ARGUMENTS)
+ message(STATUS "Ctulu -- Adding sources from files")
+ target_sources(${target_name} ${target_option} ${ctulu_target_sources_FILES} ${ctulu_target_sources_UNPARSED_ARGUMENTS})
+ endif()
+
+ if(ctulu_target_sources_DIRS)
+ message(STATUS "Ctulu -- Adding sources from directories")
+ if(ctulu_target_sources_NORECURSE)
+ ctulu_create_file_architecture(sources ${ctulu_target_sources_DIRS} NORECURSE)
+ else()
+ ctulu_create_file_architecture(sources ${ctulu_target_sources_DIRS})
+ endif()
+ target_sources(${target_name} ${target_option} ${sources})
+ endif()
+endfunction()
\ No newline at end of file
diff --git a/cmake/ctulu_warnings.cmake b/cmake/ctulu_warnings.cmake
new file mode 100644
index 0000000..e43dd04
--- /dev/null
+++ b/cmake/ctulu_warnings.cmake
@@ -0,0 +1,89 @@
+include(CheckCXXCompilerFlag)
+include(CheckCCompilerFlag)
+
+## ctulu_target_warning_from_file(target_name warning_file)
+## Add warnings from file to the given target.
+# {value} [in] target_name: Name of the target
+# {value} [in] warning_file: File where the warning are.
+# If the file is updated, cmake when a new build is launched.
+function(ctulu_target_warning_from_file target_name warning_file)
+ get_filename_component(warning_filename ${warning_file} NAME)
+ configure_file(${warning_file} ${CMAKE_BINARY_DIR}/ctulu/${warning_filename} COPYONLY)
+ file(STRINGS ${warning_file} warning_file_content)
+ set(configurations)
+ set(languages)
+
+ message(STATUS "Ctulu -- Stating parsing warnings in \"${warning_file}\"")
+ foreach(line ${warning_file_content})
+ # 'begin-line' 'spaces*' [ 'spaces*' 'string' 'spaces*' 'number' ] 'spaces*' 'end-line'
+ string(REGEX MATCH "^[ ]*\\[[ ]*([A-Za-z0-9_]*)[ ]*[ ]*][ ]*$" tmp_line ${line})
+ if(tmp_line)
+ if(compiler)
+ message(STATUS "Ctulu -- Parsing warnings for \"${compiler}\" finished")
+ endif()
+ message(STATUS "Ctulu -- Parsing warnings for \"${CMAKE_MATCH_1}\"")
+ set(compiler ${CMAKE_MATCH_1})
+ continue()
+ else()
+ if(NOT DEFINED warning_printed AND NOT DEFINED compiler)
+ message(WARNING "Ctulu -- Please specify compiler first.\nLines will be ignored until compiler is specified.")
+ set(warning_printed ON)
+ endif()
+ endif()
+
+ # 'begin-line' 'spaces*' [ 'spaces*' 'string*' 'spaces*' level 'spaces*' 'number' ] 'spaces*' 'end-line'
+ string(REGEX MATCH "^[ ]*\\[[ ]*([A-Za-z0-9_;]*)[ ]*level[ ]*([0-9]*)[ ]*[:]?[ ]*([A-Za-z0-9_;]*)[ ]*][ ]*$" tmp_line ${line})
+ if(tmp_line)
+ set(list_to_inserts)
+ set(current_languages ${CMAKE_MATCH_1})
+ set(warning_level ${CMAKE_MATCH_2})
+ set(build_type_list ${CMAKE_MATCH_3})
+
+ if(NOT current_languages)
+ set(current_languages c cxx)
+ endif()
+
+ foreach(language ${current_languages})
+ if(NOT ${language} IN_LIST languages)
+ list(APPEND languages ${language})
+ endif()
+
+ foreach(build_type ${build_type_list})
+ string(TOUPPER ${build_type} build_type)
+ if(NOT ${build_type} IN_LIST configurations)
+ list(APPEND configurations ${build_type})
+ endif()
+ if(NOT ${warning_level} IN_LIST ctulu_${target_name}_${compiler}_${language}_${build_type}_warnings_levels)
+ list(APPEND ctulu_${target_name}_${compiler}_${language}_${build_type}_warnings_levels ${warning_level})
+ endif()
+
+ list(APPEND list_to_inserts ctulu_${target_name}_${compiler}_${language}_${build_type}_warnings_level_${warning_level})
+ endforeach()
+ endforeach()
+ else()
+ string(REGEX MATCH "^[ ]*#" tmp_line ${line})
+ if(NOT tmp_line)
+ string(STRIP ${line} line)
+ foreach(list ${list_to_inserts})
+ list(APPEND ${list} ${line})
+ endforeach()
+ endif()
+ endif()
+ endforeach()
+ if(compiler)
+ message(STATUS "Ctulu -- Parsing warnings for \"${compiler}\" finished")
+ endif()
+ message(STATUS "Ctulu -- Parsing finished")
+
+ foreach(language ${languages})
+ foreach(configuration ${configurations})
+ foreach(it ${ctulu_${target_name}_gcc_${language}_${configuration}_warnings_levels})
+ if(ctulu_${target_name}_warning_level GREATER_EQUAL it)
+ foreach(warning ${ctulu_${target_name}_gcc_${language}_${configuration}_warnings_level_${it}})
+ ctulu_target_compiler_flag(${target_name} ${warning} ${language} ${configuration})
+ endforeach()
+ endif()
+ endforeach()
+ endforeach()
+ endforeach()
+endfunction()
diff --git a/cmake/get_external_dependency.cmake b/cmake/get_external_dependency.cmake
deleted file mode 100644
index 9d941bc..0000000
--- a/cmake/get_external_dependency.cmake
+++ /dev/null
@@ -1,20 +0,0 @@
-
-function(get_external_dependency projectname external_file)
- configure_file(${external_file} ${projectname}-download/CMakeLists.txt)
- execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
- RESULT_VARIABLE result
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${projectname}-download)
- if(result)
- message(FATAL_ERROR "CMake step failed: ${result}")
- endif()
- execute_process(COMMAND ${CMAKE_COMMAND} --build .
- RESULT_VARIABLE result
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${projectname}-download)
- if(result)
- message(FATAL_ERROR "Build step failed: ${result}")
- endif()
-
- add_subdirectory(${CMAKE_CURRENT_BINARY_DIR}/${projectname}-src
- ${CMAKE_CURRENT_BINARY_DIR}/${projectname}-build
- EXCLUDE_FROM_ALL)
-endfunction()
\ No newline at end of file
diff --git a/cmake/setup_output_dirs.cmake b/cmake/setup_output_dirs.cmake
new file mode 100644
index 0000000..cbcea00
--- /dev/null
+++ b/cmake/setup_output_dirs.cmake
@@ -0,0 +1,17 @@
+# place generated binaries in bin
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin/)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/)
+
+# place generated libs in lib
+file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib/)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/lib/)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/lib/)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/lib/)
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib/)
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/lib/)
\ No newline at end of file
diff --git a/cmake/warnings.txt b/cmake/warnings.txt
new file mode 100644
index 0000000..9fb6ed5
--- /dev/null
+++ b/cmake/warnings.txt
@@ -0,0 +1,159 @@
+[gcc]
+ [cxx level 1 Debug;Release]
+ -pedantic
+ -Werror=pedantic
+ -Wall
+ -Wextra
+
+ [cxx level 2 Debug]
+ ## Extra flags:
+ -Wdouble-promotion
+ -Wnull-dereference
+ -Wimplicit-fallthrough
+ -Wif-not-aligned
+ -Wmissing-include-dirs
+ -Wswitch-bool
+ -Wswitch-unreachable
+ -Walloc-zero
+ -Wduplicated-branches
+ -Wduplicated-cond
+ -Wfloat-equal
+ -Wshadow
+ -Wundef
+ -Wexpansion-to-defined
+ #-Wunused-macros
+ -Wcast-qual
+ -Wcast-align
+ -Wwrite-strings
+ -Wconversion
+ -Wsign-conversion
+ -Wdate-time
+ -Wextra-semi
+ -Wlogical-op
+ -Wmissing-declarations
+ -Wredundant-decls
+ -Wrestrict
+ #-Winline
+ -Winvalid-pch
+ -Woverlength-strings
+ -Wformat=2
+ -Wformat-signedness
+ -Winit-self
+ -Wold-style-cast
+
+ ## Optimisation dependant flags
+ -Wstrict-overflow=5
+
+ ## Info flags
+ #-Winvalid-pch
+ #-Wvolatile-register-var
+ -Wdisabled-optimization
+ #-Woverlength-strings
+ #-Wunsuffixed-float-constants
+ #-Wvector-operation-performance
+
+ ## Apocalypse flags:
+ #-Wsystem-headers
+ #-Werror
+
+ ## Exit on first error
+ -Wfatal-errors
+
+[clang]
+ [cxx level 1 Release;Debug]
+ ## Base flags:
+ -pedantic
+ -Werror=pedantic
+ -Wall
+ -Wextra
+
+ [cxx level 2 Debug]
+ ## Extra flags:
+ -Wbad-function-cast
+ -Wcomplex-component-init
+ -Wconditional-uninitialized
+ -Wcovered-switch-default
+ -Wcstring-format-directive
+ -Wdelete-non-virtual-dtor
+ -Wdeprecated
+ -Wdollar-in-identifier-extension
+ -Wdouble-promotion
+ -Wduplicate-enum
+ -Wduplicate-method-arg
+ -Wembedded-directive
+ -Wexpansion-to-defined
+ -Wextended-offsetof
+ -Wfloat-conversion
+ -Wfloat-equal
+ -Wfor-loop-analysis
+ -Wformat-pedantic
+ -Wgnu
+ -Wimplicit-fallthrough
+ -Winfinite-recursion
+ -Winvalid-or-nonexistent-directory
+ -Wkeyword-macro
+ -Wmain
+ -Wmethod-signatures
+ -Wmicrosoft
+ -Wmismatched-tags
+ -Wmissing-field-initializers
+ -Wmissing-method-return-type
+ -Wmissing-prototypes
+ -Wmissing-variable-declarations
+ -Wnested-anon-types
+ -Wnon-virtual-dtor
+ -Wnonportable-system-include-path
+ -Wnull-pointer-arithmetic
+ -Wnullability-extension
+ -Wold-style-cast
+ -Woverriding-method-mismatch
+ -Wpacked
+ -Wpedantic
+ -Wpessimizing-move
+ -Wredundant-move
+ -Wreserved-id-macro
+ -Wself-assign
+ -Wself-move
+ -Wsemicolon-before-method-body
+ -Wshadow
+ -Wshadow-field
+ -Wshadow-field-in-constructor
+ -Wshadow-uncaptured-local
+ -Wshift-sign-overflow
+ -Wshorten-64-to-32
+ #-Wsign-compare
+ #-Wsign-conversion
+ -Wsigned-enum-bitfield
+ -Wstatic-in-inline
+ #-Wstrict-prototypes
+ #-Wstring-conversion
+ #-Wswitch-enum
+ -Wtautological-compare
+ -Wtautological-overlap-compare
+ -Wthread-safety
+ -Wundefined-reinterpret-cast
+ -Wuninitialized
+ #-Wunknown-pragmas
+ -Wunreachable-code
+ -Wunreachable-code-aggressive
+ #-Wunused
+ -Wunused-const-variable
+ -Wunused-lambda-capture
+ -Wunused-local-typedef
+ -Wunused-parameter
+ -Wunused-private-field
+ -Wunused-template
+ -Wunused-variable
+ -Wused-but-marked-unused
+ -Wzero-as-null-pointer-constant
+ -Wzero-length-array
+
+ ## Lifetime
+ -Wlifetime
+
+ ## Info flags
+ -Wcomma
+ -Wcomment
+
+ ## Exit on first error
+ -Wfatal-errors
\ No newline at end of file
diff --git a/example/main.cpp b/example/main.cpp
deleted file mode 100644
index 01e85d3..0000000
--- a/example/main.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-int main()
-{
- constexpr int nbrThreads = 64;
-
- {
- std::shared_future f;
- std::promise ready_promise;
- f = std::shared_future(ready_promise.get_future());
-
- wf::unordered_map m(8);
-
- std::array threads;
- std::array insertion_times = {};
- for (std::size_t i = 0; i < nbrThreads; ++i)
- {
- threads[i] = std::thread([&m, i, &f, &insertion_times]() {
- f.wait();
-
- clock_t t_start = std::clock();
- bool inserted = m.insert(i, i);
- clock_t t_end = std::clock();
- insertion_times[i] = 1000.0 * static_cast(t_end - t_start) / CLOCKS_PER_SEC;
- if (!inserted)
- {
- printf("%zu, %d\n", i, inserted);
- }
- });
- }
-
- {
- using namespace std::chrono_literals;
- std::this_thread::sleep_for(1s);
- }
-
- ready_promise.set_value();
-
- for (auto& t: threads)
- {
- t.join();
- }
-
- double max = *std::max_element(insertion_times.begin(), insertion_times.end());
- std::cout << "Max: " << max << "ms\n";
- std::cout << "Mean: "
- << std::accumulate(insertion_times.begin(), insertion_times.end(), 0.0) / insertion_times.size()
- << "ms\n";
- std::cout << "Min: " << *std::min_element(insertion_times.begin(), insertion_times.end()) << "ms\n";
- }
-
- return 0;
-}
\ No newline at end of file
diff --git a/examples/unordered_map_example.cpp b/examples/unordered_map_example.cpp
new file mode 100644
index 0000000..9fcd8e4
--- /dev/null
+++ b/examples/unordered_map_example.cpp
@@ -0,0 +1,63 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+constexpr int nbr_threads = 64;
+
+int main()
+{
+ std::promise ready_promise;
+ std::shared_future f = {ready_promise.get_future()};
+
+ wfc::unordered_map m(8, nbr_threads, nbr_threads);
+
+ std::array threads;
+ std::array insertion_times = {};
+ for (std::size_t i = 0; i < nbr_threads; ++i)
+ {
+ threads[i] = std::thread([&m, i, &f, &insertion_times]() {
+ f.wait();
+
+ clock_t t_start = std::clock();
+ wfc::operation_result result = m.insert(i, i);
+ clock_t t_end = std::clock();
+ insertion_times[i] = 1000.0 * static_cast(t_end - t_start) / CLOCKS_PER_SEC;
+ if (failed(result))
+ {
+ std::stringstream output;
+ output << "Not inserted: " << i << '\n';
+ std::cout << output.str();
+ }
+ });
+ }
+
+ {
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(1s);
+ }
+
+ ready_promise.set_value();
+
+ for (auto& t: threads)
+ {
+ t.join();
+ }
+
+ m.visit(
+ [](std::pair p) { std::cout << '[' << p.first << '-' << p.second << "]\n"; });
+
+ double max = *std::max_element(insertion_times.begin(), insertion_times.end());
+ std::cout << "Max: " << max << "ms\n";
+ std::cout << "Mean: "
+ << std::accumulate(insertion_times.begin(), insertion_times.end(), 0.0) / insertion_times.size()
+ << "ms\n";
+ std::cout << "Min: " << *std::min_element(insertion_times.begin(), insertion_times.end()) << "ms\n";
+
+ return 0;
+}
diff --git a/external_libs/google_test.cmake b/external_libs/google_test.cmake
deleted file mode 100644
index d3c1d55..0000000
--- a/external_libs/google_test.cmake
+++ /dev/null
@@ -1,15 +0,0 @@
-cmake_minimum_required(VERSION 2.8.2)
-
-project(googletest-download NONE)
-
-include(ExternalProject)
-ExternalProject_Add(googletest
- GIT_REPOSITORY https://github.com/google/googletest.git
- GIT_TAG master
- SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-src"
- BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/googletest-build"
- CONFIGURE_COMMAND ""
- BUILD_COMMAND ""
- INSTALL_COMMAND ""
- TEST_COMMAND ""
- )
\ No newline at end of file
diff --git a/include/wait_free_unordered_map.hpp b/include/wait_free_unordered_map.hpp
deleted file mode 100644
index 46790e2..0000000
--- a/include/wait_free_unordered_map.hpp
+++ /dev/null
@@ -1,623 +0,0 @@
-#ifndef WF_WAIT_FREE_UNORDERED_MAP
-#define WF_WAIT_FREE_UNORDERED_MAP
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-namespace wf
-{
- template
- T clz(T x)
- {
- if constexpr (std::is_same_v)
- {
- return static_cast(__builtin_clz(x));
- }
- else if constexpr (std::is_same_v)
- {
- return static_cast(__builtin_clzl(x));
- }
- else if constexpr (std::is_same_v)
- {
- return static_cast(__builtin_clzll(x));
- }
- else
- {
- static_assert(!std::is_same_v);
- }
- }
-
- template
- class identity_hash
- {
- public:
- std::size_t operator()(const Key& k) const
- {
- return static_cast(k);
- }
- };
-
- template >
- class unordered_map
- {
- public:
- using hash_t = std::invoke_result_t;
- using value_t = Value;
-
- explicit unordered_map(std::size_t initialSize);
-
- unordered_map(const unordered_map&) = delete;
-
- unordered_map& operator=(const unordered_map&) = delete;
-
- bool insert(const Key& key, const Value& value);
-
- std::optional get(const Key& key);
-
- template
- void visit(VisitorFun&& fun) noexcept(
- noexcept(std::is_nothrow_invocable_v>));
-
- std::size_t size() const noexcept;
-
- bool is_empty() const noexcept;
-
- private:
- struct node__;
- struct arraynode__;
- union node_ptr;
-
- using node_t = node__;
- using arraynode_t = arraynode__;
-
- struct node__
- {
- hash_t hash;
- value_t value;
- };
-
- struct arraynode__
- {
- using value_t = std::atomic;
- using reference_t = value_t&;
-
- explicit arraynode__(std::size_t size);
-
- arraynode__(const arraynode__&) = delete;
- ~arraynode__();
-
- arraynode__& operator=(const arraynode__&) = delete;
-
- reference_t operator[](std::size_t i);
-
- private:
- value_t* m_ptr;
- std::size_t m_size;
- };
-
- union node_ptr
- {
- node_ptr() noexcept;
- node_ptr(node_t* datanode) noexcept;
- node_ptr(arraynode_t* arraynode) noexcept;
-
- node_t* datanode_ptr;
-
- arraynode_t* arraynode_ptr;
-
- std::uintptr_t ptr_int;
- };
-
- node_ptr allocate_node(hash_t hash, value_t value) const;
-
- node_ptr expandNode(node_ptr arraynode, std::size_t position, std::size_t level) noexcept;
-
- template
- void visit_array_node(node_ptr node, std::size_t level, VisitorFun&& fun) noexcept(
- noexcept(std::is_nothrow_invocable_v>));
-
- constexpr static std::size_t log2_power_two(std::size_t x) noexcept;
-
- static bool is_power_of_two(std::size_t nbr) noexcept;
-
- static node_ptr mark_datanode(node_ptr arraynode, std::size_t position) noexcept;
-
- static void mark_datanode(node_ptr& node) noexcept;
-
- static void unmark_datanode(node_ptr& node) noexcept;
-
- static bool is_marked(node_ptr node) noexcept;
-
- static void mark_arraynode(node_ptr& node) noexcept;
-
- static void unmark_arraynode(node_ptr& node) noexcept;
-
- static bool is_array_node(node_ptr node) noexcept;
-
- static node_ptr get_node(node_ptr arraynode, std::size_t pos) noexcept;
-
- static node_ptr sanitize_ptr(node_ptr arraynode) noexcept;
-
- arraynode_t m_head;
- std::size_t m_head_size;
- std::size_t m_arrayLength;
- std::atomic m_size;
- static constexpr std::size_t hash_size_in_bits = sizeof(hash_t) * std::numeric_limits::digits;
- };
-
- template
- unordered_map::node_ptr::node_ptr() noexcept : datanode_ptr{nullptr}
- {
- }
-
- template
- unordered_map::node_ptr::node_ptr(node_t* datanode) noexcept : datanode_ptr{datanode}
- {
- }
-
- template
- unordered_map::node_ptr::node_ptr(arraynode_t* arraynode) noexcept
- : arraynode_ptr{arraynode}
- {
- }
-
- template
- unordered_map::arraynode__::arraynode__(std::size_t size)
- : m_ptr{new value_t[size]}, m_size(size)
- {
- for (std::size_t i = 0; i < size; ++i)
- {
- new (&m_ptr[i]) value_t();
- }
- }
-
- template
- unordered_map::arraynode__::~arraynode__()
- {
- for (std::size_t i = 0; i < m_size; ++i)
- {
- node_ptr child = m_ptr[i].load();
- if (child.arraynode_ptr != nullptr)
- {
- if (is_array_node(child))
- {
- delete sanitize_ptr(child).arraynode_ptr;
- }
- else
- {
- delete child.datanode_ptr;
- }
- }
- }
-
- delete[] m_ptr;
- }
-
- template
- auto unordered_map::arraynode__::operator[](std::size_t i) -> value_t&
- {
- return m_ptr[i];
- }
-
- template
- unordered_map::unordered_map(std::size_t initialSize)
- : m_head(2UL << initialSize), m_head_size(2UL << initialSize), m_arrayLength(initialSize), m_size(0UL)
- {
- static_assert(std::atomic::is_always_lock_free, "Atomic implementation is not lock free");
- static_assert(std::atomic::is_always_lock_free, "Atomic implementation is not lock free");
-
- if (!is_power_of_two(initialSize))
- {
- throw std::runtime_error("Size should be a power of four");
- }
- }
-
- template
- bool unordered_map::insert(const Key& key, const Value& value)
- {
- std::size_t array_pow = log2_power_two(m_arrayLength);
-
- std::size_t position;
- std::size_t failCount;
- node_ptr local;
- local.arraynode_ptr = &m_head;
- mark_arraynode(local);
-
- hash_t fullhash = HashFunction{}(key);
- hash_t hash = fullhash;
-
- for (std::size_t r = 0; r < hash_size_in_bits - array_pow; r += array_pow)
- {
- if (r == 0)
- {
- position = hash & (m_head_size - 1);
- hash >>= m_arrayLength;
- }
- else
- {
- position = hash & (m_arrayLength - 1);
- hash >>= array_pow;
- }
-
- failCount = 0;
-
- node_ptr node = get_node(local, position);
-
- while (true)
- {
- if (failCount > 8)
- { // FIXME
- node = mark_datanode(local, position);
- }
- if (node.datanode_ptr == nullptr)
- {
- node_ptr node_to_insert = allocate_node(fullhash, value);
-
- node_ptr null;
- null.datanode_ptr = nullptr;
-
- if ((*sanitize_ptr(local).arraynode_ptr)[position].compare_exchange_weak(null, node_to_insert))
- {
- // watch(nullptr);
- ++m_size;
- return true;
- }
- else
- {
- delete node_to_insert.datanode_ptr;
- }
- }
-
- if (is_marked(node))
- {
- node = expandNode(local, position, r);
- }
- if (is_array_node(node))
- {
- local = node;
- break;
- }
- else
- {
- node_ptr node2 = get_node(local, position);
- if (node.ptr_int != node2.ptr_int)
- {
- ++failCount;
- node = node2;
- }
- else
- {
- if (node.datanode_ptr->hash == fullhash)
- {
- // watch(nullptr);
- return false;
- }
- else
- {
- node = expandNode(local, position, r);
- if (is_array_node(node))
- {
- local = node;
- break;
- }
- else
- {
- ++failCount;
- }
- }
- }
- }
- }
- }
-
- // watch(nullptr);
- position = hash & (m_arrayLength - 1);
- node_ptr node = get_node(local, position);
-
- if (node.datanode_ptr == nullptr)
- {
- node_ptr node_to_insert = allocate_node(fullhash, value);
-
- node_ptr null;
- null.datanode_ptr = nullptr;
-
- return (*sanitize_ptr(local).arraynode_ptr)[position].compare_exchange_weak(null, node_to_insert);
- }
- else
- {
- return false;
- }
- }
-
- template
- auto unordered_map::allocate_node(hash_t hash, value_t value) const
- -> unordered_map::node_ptr
- {
- node_ptr node_to_insert;
-
- node_to_insert.datanode_ptr = new (std::align_val_t{8}) node_t{};
- node_to_insert.datanode_ptr->hash = hash;
- node_to_insert.datanode_ptr->value = value;
-
- return node_to_insert;
- }
-
- template
- std::optional unordered_map::get(const Key& key)
- {
- std::size_t array_pow = log2_power_two(m_arrayLength);
-
- std::size_t position;
- std::size_t failCount;
- node_ptr local;
- local.arraynode_ptr = &m_head;
- mark_arraynode(local);
-
- hash_t fullhash = HashFunction{}(key);
- hash_t hash = fullhash;
-
- for (std::size_t r = 0; r < hash_size_in_bits - array_pow; r += array_pow)
- {
- if (r == 0)
- {
- position = hash & (m_head_size - 1);
- hash >>= m_arrayLength;
- }
- else
- {
- position = hash & (m_arrayLength - 1);
- hash >>= array_pow;
- }
-
- failCount = 0;
-
- node_ptr node = get_node(local, position);
-
- while (true)
- {
- if (failCount > 8)
- { // FIXME
- node = mark_datanode(local, position);
- }
- if (node.datanode_ptr == nullptr)
- {
- return {};
- }
-
- if (is_marked(node))
- {
- node = expandNode(local, position, r);
- }
- if (is_array_node(node))
- {
- local = node;
- break;
- }
- else
- {
- node_ptr node2 = get_node(local, position);
- if (node.ptr_int != node2.ptr_int)
- {
- ++failCount;
- node = node2;
- continue;
- }
- else
- {
- if (node.datanode_ptr->hash == fullhash)
- {
- // watch(nullptr);
- return {node.datanode_ptr->value};
- }
- else
- {
- node = expandNode(local, position, r);
- if (is_array_node(node))
- {
- local = node;
- break;
- }
- else
- {
- ++failCount;
- }
- }
- }
- }
- }
- }
-
- return {};
- }
-
- template
- bool unordered_map::is_power_of_two(std::size_t nbr) noexcept
- {
- return nbr && !(nbr & (nbr - 1));
- }
-
- template
- constexpr std::size_t unordered_map::log2_power_two(std::size_t x) noexcept
- {
- return hash_size_in_bits - clz(x) - 1UL;
- }
-
- template
- auto unordered_map::mark_datanode(node_ptr arraynode, std::size_t position) noexcept
- -> unordered_map::node_ptr
- {
- node_ptr oldValue = get_node(arraynode, position);
- node_ptr value = oldValue;
- mark_datanode(value);
-
- (*sanitize_ptr(arraynode).arraynode_ptr)[position].compare_exchange_weak(oldValue, value);
-
- return get_node(arraynode, position);
- }
-
- template
- bool unordered_map::is_marked(node_ptr node) noexcept
- {
- return static_cast(node.ptr_int & 0b1UL);
- }
-
- template
- bool unordered_map::is_array_node(node_ptr node) noexcept
- {
- return static_cast(node.ptr_int & 0b10UL);
- }
-
- template
- auto unordered_map::expandNode(node_ptr arraynode,
- std::size_t position,
- std::size_t level) noexcept -> unordered_map::node_ptr
- {
- std::atomic& node_atomic = (*sanitize_ptr(arraynode).arraynode_ptr)[position];
- node_ptr old_value = node_atomic.load();
-
- // watch(value);
- if (is_array_node(old_value))
- {
- return old_value;
- }
- node_ptr value = node_atomic.load();
-
- if (value.ptr_int != old_value.ptr_int)
- {
- return value;
- }
-
- node_ptr new_value;
- new_value.arraynode_ptr = new (std::align_val_t{8}) arraynode_t{m_arrayLength};
-
- std::size_t new_pos = value.datanode_ptr->hash >> (m_arrayLength + level) & (m_arrayLength - 1);
- unmark_datanode(value);
-
- (*new_value.arraynode_ptr)[new_pos] = value;
- mark_arraynode(new_value);
-
- if (!node_atomic.compare_exchange_weak(old_value, new_value))
- {
- new_value = sanitize_ptr(new_value);
- (*new_value.arraynode_ptr)[new_pos] = node_ptr{};
- delete new_value.arraynode_ptr;
- }
-
- return new_value;
- }
-
- template
- template
- void unordered_map::visit(VisitorFun&& fun) noexcept(
- noexcept(std::is_nothrow_invocable_v>))
- {
- for (std::size_t i = 0; i < m_head_size; ++i)
- {
- node_ptr node = m_head[i].load();
- if (node.datanode_ptr != nullptr)
- {
- if (is_array_node(node))
- {
- visit_array_node(node, 1, fun);
- }
- else
- {
- std::invoke(
- fun,
- std::pair(node.datanode_ptr->hash, node.datanode_ptr->value));
- }
- }
- }
- }
-
- template
- template
- void unordered_map::visit_array_node(
- node_ptr node,
- std::size_t level,
- VisitorFun&&
- fun) noexcept(noexcept(std::is_nothrow_invocable_v>))
- {
- for (std::size_t i = 0; i < m_arrayLength; ++i)
- {
- node_ptr child = get_node(node, i);
- if (child.datanode_ptr != nullptr)
- {
- if (is_array_node(child))
- {
- visit_array_node(child, level + 1);
- }
- else
- {
- std::invoke(
- fun,
- std::pair(child.datanode_ptr->hash, child.datanode_ptr->value));
- }
- }
- }
- }
-
- template
- auto unordered_map::get_node(node_ptr arraynode, std::size_t pos) noexcept
- -> unordered_map::node_ptr
- {
- assert(is_array_node(arraynode));
-
- node_ptr accessor = sanitize_ptr(arraynode);
-
- return (*accessor.arraynode_ptr)[pos].load();
- }
-
- template
- auto unordered_map::sanitize_ptr(node_ptr arraynode) noexcept -> node_ptr
- {
- unmark_arraynode(arraynode);
- return arraynode;
- }
-
- template
- void unordered_map::mark_datanode(node_ptr& node) noexcept
- {
- node.ptr_int |= 0b01UL;
- }
-
- template
- void unordered_map::unmark_datanode(node_ptr& node) noexcept
- {
- node.ptr_int &= ~0b01UL;
- }
-
- template
- void unordered_map::mark_arraynode(node_ptr& node) noexcept
- {
- node.ptr_int |= 0b10UL;
- }
-
- template
- void unordered_map::unmark_arraynode(node_ptr& node) noexcept
- {
- node.ptr_int &= ~0b10UL;
- }
-
- template
- std::size_t unordered_map::size() const noexcept
- {
- return m_size;
- }
-
- template
- bool unordered_map::is_empty() const noexcept
- {
- return size() == 0;
- }
-
-} // namespace wf
-
-#endif
\ No newline at end of file
diff --git a/include/wfc/details/unordered_map/nodes.hpp b/include/wfc/details/unordered_map/nodes.hpp
new file mode 100644
index 0000000..545b0cd
--- /dev/null
+++ b/include/wfc/details/unordered_map/nodes.hpp
@@ -0,0 +1,209 @@
+#ifndef WFC_NODES_HPP
+#define WFC_NODES_HPP
+
+#include
+#include
+
+namespace wfc
+{
+ namespace details
+ {
+ template
+ union node_union;
+
+ template
+ struct node_t
+ {
+ Hash hash;
+ Key key;
+ Value value;
+ };
+
+ template
+ struct arraynode_t
+ {
+ using value_t = std::atomic>;
+ using reference_t = value_t&;
+ using const_reference_t = const value_t&;
+
+ explicit arraynode_t(std::size_t size);
+
+ arraynode_t(const arraynode_t&) noexcept = delete;
+ ~arraynode_t() noexcept;
+
+ arraynode_t& operator=(const arraynode_t&) = delete;
+
+ reference_t operator[](std::size_t i) noexcept;
+
+ const_reference_t operator[](std::size_t i) const noexcept;
+
+ private:
+ value_t* m_ptr;
+ std::size_t m_size;
+ };
+
+ template
+ union node_union
+ {
+ node_union() noexcept;
+ explicit node_union(NodeT* datanode) noexcept;
+ explicit node_union(arraynode_t* arraynode) noexcept;
+
+ NodeT* datanode_ptr;
+ arraynode_t* arraynode_ptr;
+ std::uintptr_t ptr_int;
+ };
+
+ template
+ bool is_array_node(node_union node) noexcept;
+
+ template
+ node_union mark_datanode(node_union arraynode, std::size_t position) noexcept;
+
+ template
+ void mark_datanode(node_union& node) noexcept;
+
+ template
+ void unmark_datanode(node_union& node) noexcept;
+
+ template
+ bool is_marked(node_union node) noexcept;
+
+ template
+ void mark_arraynode(node_union& node) noexcept;
+
+ template
+ void unmark_arraynode(node_union& node) noexcept;
+
+ template
+ node_union get_node(node_union arraynode, std::size_t pos) noexcept;
+
+ template
+ node_union sanitize_ptr(node_union arraynode) noexcept;
+
+ template
+ bool is_array_node(node_union node) noexcept
+ {
+ return static_cast(node.ptr_int & 0b10UL);
+ }
+
+ template
+ auto mark_datanode(node_union arraynode, std::size_t position) noexcept -> node_union
+ {
+ node_union oldValue = get_node(arraynode, position);
+ node_union value = oldValue;
+ mark_datanode(value);
+
+ (*sanitize_ptr(arraynode).arraynode_ptr)[position].compare_exchange_weak(oldValue, value);
+
+ return get_node(arraynode, position);
+ }
+
+ template
+ void mark_datanode(node_union& node) noexcept
+ {
+ node.ptr_int |= 0b01UL;
+ }
+
+ template
+ void unmark_datanode(node_union& node) noexcept
+ {
+ node.ptr_int &= ~0b01UL;
+ }
+
+ template
+ bool is_marked(node_union node) noexcept
+ {
+ return static_cast(node.ptr_int & 0b1UL);
+ }
+
+ template
+ void mark_arraynode(node_union& node) noexcept
+ {
+ node.ptr_int |= 0b10UL;
+ }
+
+ template
+ void unmark_arraynode(node_union& node) noexcept
+ {
+ node.ptr_int &= ~0b10UL;
+ }
+
+ template
+ auto get_node(node_union arraynode, std::size_t pos) noexcept -> node_union
+ {
+ assert(is_array_node(arraynode));
+
+ node_union accessor = sanitize_ptr(arraynode);
+
+ return (*accessor.arraynode_ptr)[pos].load();
+ }
+
+ template
+ auto sanitize_ptr(node_union arraynode) noexcept -> node_union
+ {
+ unmark_arraynode(arraynode);
+ return arraynode;
+ }
+
+ template
+ node_union::node_union() noexcept : datanode_ptr{nullptr}
+ {
+ }
+
+ template
+ node_union::node_union(NodeT* datanode) noexcept : datanode_ptr{datanode}
+ {
+ }
+
+ template
+ node_union::node_union(arraynode_t* arraynode) noexcept : arraynode_ptr{arraynode}
+ {
+ }
+
+ template
+ arraynode_t::arraynode_t(std::size_t size) : m_ptr{new value_t[size]}, m_size(size)
+ {
+ for (std::size_t i = 0; i < size; ++i)
+ {
+ new (&m_ptr[i]) value_t();
+ }
+ }
+
+ template
+ arraynode_t::~arraynode_t() noexcept
+ {
+ for (std::size_t i = 0; i < m_size; ++i)
+ {
+ node_union child = m_ptr[i].load();
+ if (child.arraynode_ptr != nullptr)
+ {
+ if (is_array_node(child))
+ {
+ delete sanitize_ptr(child).arraynode_ptr;
+ }
+ else
+ {
+ delete child.datanode_ptr;
+ }
+ }
+ m_ptr[i].store({});
+ }
+
+ delete[] m_ptr;
+ }
+
+ template
+ auto arraynode_t::operator[](std::size_t i) noexcept -> reference_t
+ {
+ return m_ptr[i];
+ }
+
+ template
+ auto arraynode_t::operator[](std::size_t i) const noexcept -> const_reference_t
+ {
+ return m_ptr[i];
+ }
+ } // namespace details
+} // namespace wfc
+#endif // WFC_NODES_HPP
diff --git a/include/wfc/unordered_map.hpp b/include/wfc/unordered_map.hpp
new file mode 100644
index 0000000..b2a3271
--- /dev/null
+++ b/include/wfc/unordered_map.hpp
@@ -0,0 +1,787 @@
+#ifndef WFC_UNORDERED_MAP_HPP
+#define WFC_UNORDERED_MAP_HPP
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "utility/math.hpp"
+#include "details/unordered_map/nodes.hpp"
+
+namespace wfc
+{
+ /**
+ * @brief represents the return status of the operation.
+ */
+ enum class operation_result
+ {
+ success, /**< operation successful */
+ expected_value_mismatch, /**< the key's associated value doesn't match the expected value */
+ element_not_found, /**< the key is not present in the hash map */
+ already_present /**< the key is already present in the hash map */
+ };
+
+ /**
+ * Helper function that returns true if the operation_result is a success.
+ */
+ [[nodiscard]] constexpr bool succeeded(operation_result e) noexcept
+ {
+ return e == operation_result::success;
+ }
+
+ /**
+ * Helper function that returns true if the operation_result correspond to a failed state.
+ */
+ [[nodiscard]] constexpr bool failed(operation_result e) noexcept
+ {
+ return !succeeded(e);
+ }
+
+ /**
+ * Default hash function. This function is the identity.
+ * @tparam Key - Type of the key
+ */
+ template
+ class identity_hash
+ {
+ public:
+ static_assert(std::is_fundamental_v, "Key should be a fundamental type to use the default hash");
+ Key operator()(const Key& k) const noexcept
+ {
+ return k;
+ }
+ };
+
+ /**
+ * A wait free hash map
+ *
+ * @details This map could be seen as a n-ary tree, (except that the head has 2^n children)
+ * In the case of this map, n is array_length.
+ * Each node of this map could be an array or a datanode. If two datanodes should go in the same place
+ * the existing datanode is transformed into an arraynode to allow the insertion of the two datanodes.
+ * It is noteworthy that this expending of the map will be repeated until the hash of the two nodes differ.
+ *
+ * @tparam Key type of the key in the map
+ * @tparam Value type of the value in the map
+ * @tparam HashFunction Functor that holds the hash function to be used on keys.
+ * The hash function has to be collision-free.
+ * Consequently, output size should be at least as large as input size.
+ */
+ template >
+ class unordered_map
+ {
+ public:
+ using key_t = Key;
+ using hash_t = std::invoke_result_t;
+ using value_t = Value;
+
+ /**
+ * Constructs a wait free hash map.
+ *
+ * @param array_length size of the array containing the elements (head size is 2^array_length)
+ * @param max_fail_count this value should match to the number of threads using this map
+ * @param max_nbr_threads maximal number of thread using the hashmap.
+ */
+ explicit unordered_map(std::size_t array_length,
+ std::size_t max_fail_count = 8,
+ std::size_t max_nbr_threads = 8);
+ unordered_map(const unordered_map&) = delete;
+ ~unordered_map() noexcept = default;
+
+ unordered_map& operator=(const unordered_map&) = delete;
+
+ /**
+ * Inserts the key and the value in the hash map.
+ *
+ * @return Returns operation_result::already_present if the key is already in the map.
+ * Otherwise it returns operation_result::success
+ */
+ operation_result insert(const Key& key, const Value& value);
+
+ /**
+ * Tries to retrieve the value associated to the key.
+ *
+ * @return an empty optional if the key in not in the map. The associated value otherwise.
+ */
+ std::optional get(const Key& key);
+
+ /**
+ * Update the value associated with the given key if the current
+ * value matches expected_value.
+ *
+ * @param key
+ * @param value
+ * @param expected_value
+ * @return @see operation_result
+ */
+ operation_result update(const Key& key, const Value& new_value, const Value& expected_value);
+
+ /**
+ * Update the value associated with the given key.
+ *
+ * @param key
+ * @param value
+ * @return @see operation_result
+ */
+ operation_result update(const Key& key, const Value& value);
+
+ /**
+ * Remove the element associated to the given key if the
+ * expected_value matches the current value.
+ *
+ * @param key
+ * @return @see operation_result
+ */
+ operation_result remove(const Key& key, const Value& expected_value);
+
+ /**
+ * Remove the element associated to the given key.
+ *
+ * @param key
+ * @return @see operation_result
+ */
+ operation_result remove(const Key& key);
+
+ /**
+ * Applies functor on every element in the map.
+ * This function is NOT thread safe.
+ * @tparam VisitorFun The type should be compatible with this prototype
+ * void(const std::pair&);
+ * @param fun
+ */
+ template
+ void visit(VisitorFun&& fun) noexcept(
+ noexcept(std::is_nothrow_invocable_v>));
+
+ /**
+ * Returns the number of elements into the collection
+ */
+ std::size_t size() const noexcept;
+
+ /**
+ * Returns true if the map is empty, false otherwise.
+ */
+ bool is_empty() const noexcept;
+
+ private:
+ using node_t = details::node_t;
+ using node_union = details::node_union;
+ using arraynode_t = details::arraynode_t;
+
+ node_union allocate_node(hash_t hash, key_t key, value_t value) const;
+
+ node_union expand_node(node_union arraynode, std::size_t position, std::size_t level) noexcept;
+
+ bool try_node_insertion(node_union arraynode, std::size_t position, node_union& datanode);
+
+ template
+ operation_result update_impl(const Key& key, const Value& value, Fun&& compare_expected_value);
+
+ template
+ operation_result remove_impl(const Key& key, Fun&& compare_expected_value);
+
+ template
+ operation_result update_or_remove_impl(const Key& key,
+ CmpFun&& compare_expected_value,
+ AllocFun&& replacing_node);
+
+ void ensure_not_replaced(node_union& local, size_t position, size_t r, node_union& node);
+
+ template
+ void visit_array_node(node_union node, VisitorFun&& fun) noexcept(
+ noexcept(std::is_nothrow_invocable_v>));
+
+ std::tuple compute_pos_and_hash(size_t array_pow, hash_t lasthash, size_t level) const;
+
+ void safe_delete(node_union node_to_free);
+
+ void watch_node(node_union node) noexcept;
+
+ void clear_watched_node() noexcept;
+
+ arraynode_t m_head;
+ std::size_t m_head_size;
+ std::size_t m_arrayLength;
+ std::size_t m_max_fail_count;
+ std::size_t m_max_nbr_threads;
+ std::atomic m_size;
+ std::shared_ptr[]> m_watched_nodes;
+
+ static constexpr std::size_t hash_size_in_bits = sizeof(hash_t) * std::numeric_limits::digits;
+ };
+
+ template
+ unordered_map::unordered_map(std::size_t array_length,
+ std::size_t max_fail_count,
+ std::size_t max_nbr_threads)
+ : m_head(1UL << array_length)
+ , m_head_size(1UL << array_length)
+ , m_arrayLength(array_length)
+ , m_max_fail_count(max_fail_count)
+ , m_max_nbr_threads(max_nbr_threads)
+ , m_size(0UL)
+ , m_watched_nodes(new std::atomic[max_nbr_threads])
+ {
+ static_assert(std::atomic::is_always_lock_free, "Atomic implementation is not lock free");
+ static_assert(std::atomic::is_always_lock_free, "Atomic implementation is not lock free");
+
+ for (std::ptrdiff_t i = 0, n = static_cast(m_max_nbr_threads); i < n; ++i)
+ {
+ m_watched_nodes[i] = 0;
+ }
+
+ if (!is_power_of_two(array_length))
+ {
+ throw std::runtime_error("Array length should be a power of two");
+ }
+ }
+
+ template
+ operation_result unordered_map::insert(const Key& key, const Value& value)
+ {
+ std::size_t nbr_bits_to_shift = log2_of_power_of_two(m_arrayLength);
+
+ std::size_t position;
+ std::size_t fail_count;
+ node_union local{&m_head};
+ mark_arraynode(local);
+
+ hash_t fullhash = HashFunction{}(key);
+ hash_t hash = fullhash;
+
+ for (std::size_t r = 0; r < hash_size_in_bits - nbr_bits_to_shift; r += nbr_bits_to_shift)
+ {
+ fail_count = 0;
+ std::tie(position, hash) = compute_pos_and_hash(nbr_bits_to_shift, hash, r);
+ node_union node = get_node(local, position);
+
+ while (true)
+ {
+ if (fail_count > m_max_fail_count)
+ {
+ node = mark_datanode(local, position);
+ }
+
+ if (node.datanode_ptr == nullptr)
+ {
+ node_union new_node = allocate_node(fullhash, key, value);
+ if (try_node_insertion(local, position, new_node))
+ {
+ clear_watched_node();
+
+ return operation_result::success;
+ }
+ else
+ {
+ node = new_node;
+ }
+ }
+
+ if (is_marked(node))
+ {
+ node = expand_node(local, position, r);
+ }
+
+ if (is_array_node(node))
+ {
+ local = node;
+ break;
+ }
+ else
+ {
+ watch_node(node);
+ node_union node2 = get_node(local, position);
+ if (node.ptr_int != node2.ptr_int)
+ {
+ ++fail_count;
+ node = node2;
+ continue;
+ }
+ else if (node.datanode_ptr->hash == fullhash)
+ {
+ clear_watched_node();
+ return operation_result::already_present;
+ }
+ else
+ {
+ node = expand_node(local, position, r);
+ if (is_array_node(node))
+ {
+ local = node;
+ break;
+ }
+ else
+ {
+ ++fail_count;
+ }
+ }
+ }
+ }
+ }
+
+ clear_watched_node();
+ std::tie(position, std::ignore) = compute_pos_and_hash(position, hash, hash_size_in_bits - nbr_bits_to_shift);
+ node_union node = get_node(local, position);
+
+ node_union new_node = allocate_node(fullhash, key, value);
+ if (node.datanode_ptr == nullptr && try_node_insertion(local, position, new_node))
+ {
+ return operation_result::success;
+ }
+
+ return operation_result::already_present;
+ }
+
+ template
+ std::optional unordered_map::get(const Key& key)
+ {
+ std::size_t array_pow = log2_of_power_of_two(m_arrayLength);
+
+ std::size_t position;
+ node_union local{&m_head};
+ mark_arraynode(local);
+
+ hash_t fullhash = HashFunction{}(key);
+ hash_t hash = fullhash;
+
+ for (std::size_t r = 0; r < hash_size_in_bits - array_pow; r += array_pow)
+ {
+ std::tie(position, hash) = compute_pos_and_hash(array_pow, hash, r);
+ node_union node = get_node(local, position);
+
+ if (is_array_node(node))
+ {
+ local = node;
+ }
+ else if (node.datanode_ptr == nullptr)
+ {
+ clear_watched_node();
+ return std::nullopt;
+ }
+ else
+ {
+ watch_node(node);
+ if (node.ptr_int != get_node(local, position).ptr_int)
+ {
+ ensure_not_replaced(local, position, r, node);
+
+ if (is_array_node(node))
+ {
+ local = node;
+ }
+ else if (is_marked(node))
+ {
+ local = expand_node(local, position, r);
+ }
+ else if (node.datanode_ptr == nullptr)
+ {
+ clear_watched_node();
+ return std::nullopt;
+ }
+ }
+ else if (node.datanode_ptr->hash == fullhash)
+ {
+ std::optional result = node.datanode_ptr->value;
+ clear_watched_node();
+
+ return result;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ clear_watched_node();
+ return std::nullopt;
+ }
+
+ template