├── .clang-format ├── .clang-tidy ├── .cmake-format.yaml ├── .devcontainer ├── .dockerignore ├── Dockerfile └── devcontainer.json ├── .gitattributes ├── .github ├── actions │ └── setup_cache │ │ └── action.yml ├── constants.env └── workflows │ ├── auto-clang-format.yml │ ├── ci.yml │ └── codeql-analysis.yml ├── .gitignore ├── .gitlab-ci.yml ├── CMakeLists.txt ├── CMakePresets.json ├── Dependencies.cmake ├── ProjectOptions.cmake ├── README.md ├── README_building.md ├── README_dependencies.md ├── README_docker.md ├── cmake ├── CPM.cmake ├── Cache.cmake ├── CompilerWarnings.cmake ├── Cuda.cmake ├── Doxygen.cmake ├── Hardening.cmake ├── InterproceduralOptimization.cmake ├── LibFuzzer.cmake ├── Linker.cmake ├── PackageProject.cmake ├── PreventInSourceBuilds.cmake ├── Sanitizers.cmake ├── StandardProjectSettings.cmake ├── StaticAnalyzers.cmake ├── SystemLink.cmake ├── Tests.cmake ├── Utilities.cmake ├── VCEnvironment.cmake └── _FORTIFY_SOURCE.hpp ├── configured_files ├── CMakeLists.txt └── config.hpp.in ├── fuzz_test ├── CMakeLists.txt └── fuzz_tester.cpp ├── include └── chains │ ├── sample_library.hpp │ └── tuple.hpp ├── src ├── CMakeLists.txt ├── ftxui_sample │ ├── CMakeLists.txt │ └── main.cpp └── sample_library │ ├── CMakeLists.txt │ └── sample_library.cpp └── test ├── CMakeLists.txt ├── initial_draft.cpp └── tuple_tests.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | # Format style options described here: 2 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | 4 | # Many of the alignment and single line changes were made to facilitate setting 5 | # breakpoints on specific expressions. 6 | 7 | --- 8 | AccessModifierOffset: -4 9 | AlignAfterOpenBracket: Align 10 | AlignConsecutiveAssignments: false 11 | AlignConsecutiveDeclarations: false 12 | AlignEscapedNewlinesLeft: true 13 | AlignOperands: true 14 | AlignTrailingComments: true 15 | AllowAllParametersOfDeclarationOnNextLine: true 16 | AllowShortBlocksOnASingleLine: false 17 | AllowShortCaseLabelsOnASingleLine: false 18 | AllowShortFunctionsOnASingleLine: All 19 | AllowShortIfStatementsOnASingleLine: true 20 | AllowShortLoopsOnASingleLine: false 21 | # AlwaysBreakAfterDefinitionReturnType: TopLevel 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: true 25 | BinPackArguments: true 26 | BinPackParameters: false 27 | # BraceWrapping: 28 | # AfterClass: true 29 | # AfterControlStatement: false 30 | # AfterEnum: false 31 | # AfterFunction: false 32 | # AfterNamespace: false 33 | # AfterObjCDeclaration: false 34 | # AfterStruct: false 35 | # AfterUnion: false 36 | # BeforeCatch: false 37 | # BeforeElse: false 38 | # IndentBraces: false 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: false 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakAfterJavaFieldAnnotations: false 45 | BreakStringLiterals: false 46 | ColumnLimit: 100 47 | CommentPragmas: '^ IWYU pragma:' 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: true 52 | DerivePointerAlignment: false 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: true 56 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 57 | IncludeCategories: 58 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 59 | Priority: 2 60 | - Regex: '^(<|"(gtest|isl|json)/)' 61 | Priority: 3 62 | - Regex: '.*' 63 | Priority: 1 64 | IncludeIsMainRegex: '$' 65 | IndentCaseLabels: true 66 | IndentWidth: 4 67 | IndentWrappedFunctionNames: true 68 | JavaScriptQuotes: Leave 69 | JavaScriptWrapImports: true 70 | KeepEmptyLinesAtTheStartOfBlocks: false 71 | MacroBlockBegin: '' 72 | MacroBlockEnd: '' 73 | MaxEmptyLinesToKeep: 1 74 | NamespaceIndentation: None 75 | ObjCBlockIndentWidth: 4 76 | ObjCSpaceAfterProperty: true 77 | ObjCSpaceBeforeProtocolList: true 78 | PenaltyBreakBeforeFirstCallParameter: 19 79 | PenaltyBreakComment: 300 80 | PenaltyBreakFirstLessLess: 120 81 | PenaltyBreakString: 1000 82 | PenaltyExcessCharacter: 1000000 83 | PenaltyReturnTypeOnItsOwnLine: 1000 84 | PointerAlignment: Left 85 | ReflowComments: true 86 | SortIncludes: true 87 | SpaceAfterCStyleCast: false 88 | SpaceAfterTemplateKeyword: true 89 | SpaceBeforeAssignmentOperators: true 90 | SpaceBeforeParens: ControlStatements 91 | SpaceInEmptyParentheses: false 92 | SpacesBeforeTrailingComments: 1 93 | SpacesInAngles: false 94 | SpacesInContainerLiterals: true 95 | SpacesInCStyleCastParentheses: false 96 | SpacesInParentheses: false 97 | SpacesInSquareBrackets: false 98 | Standard: Cpp11 99 | TabWidth: 4 100 | UseTab: Never 101 | --- 102 | Language: Cpp 103 | --- 104 | Language: ObjC 105 | PointerAlignment: Right 106 | ... 107 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: "*, 3 | -abseil-*, 4 | -altera-*, 5 | -android-*, 6 | -fuchsia-*, 7 | -google-*, 8 | -llvm*, 9 | -modernize-use-trailing-return-type, 10 | -zircon-*, 11 | -readability-else-after-return, 12 | -readability-static-accessed-through-instance, 13 | -readability-avoid-const-params-in-decls, 14 | -cppcoreguidelines-non-private-member-variables-in-classes, 15 | -misc-non-private-member-variables-in-classes, 16 | " 17 | WarningsAsErrors: '' 18 | HeaderFilterRegex: '' 19 | FormatStyle: none 20 | 21 | CheckOptions: 22 | - key: readability-identifier-length.IgnoredVariableNames 23 | value: 'x|y|z' 24 | - key: readability-identifier-length.IgnoredParameterNames 25 | value: 'x|y|z' 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | additional_commands: 2 | foo: 3 | flags: 4 | - BAR 5 | - BAZ 6 | kwargs: 7 | DEPENDS: '*' 8 | HEADERS: '*' 9 | SOURCES: '*' 10 | bullet_char: '*' 11 | dangle_parens: false 12 | enum_char: . 13 | line_ending: unix 14 | line_width: 120 15 | max_pargs_hwrap: 3 16 | separate_ctrl_name_with_space: false 17 | separate_fn_name_with_space: false 18 | tab_size: 2 19 | 20 | markup: 21 | enable_markup: false 22 | -------------------------------------------------------------------------------- /.devcontainer/.dockerignore: -------------------------------------------------------------------------------- 1 | # Build directories and binary files 2 | build/ 3 | out/ 4 | cmake-build-*/ 5 | 6 | # User spesific settings 7 | CMakeUserPresets.json 8 | 9 | # IDE files 10 | .vs/ 11 | .idea/ 12 | .vscode/ 13 | !.vscode/settings.json 14 | !.vscode/tasks.json 15 | !.vscode/launch.json 16 | !.vscode/extensions.json 17 | *.swp 18 | *~ 19 | _ReSharper* 20 | *.log 21 | 22 | # OS Generated Files 23 | .DS_Store 24 | .AppleDouble 25 | .LSOverride 26 | ._* 27 | .Spotlight-V100 28 | .Trashes 29 | .Trash-* 30 | $RECYCLE.BIN/ 31 | .TemporaryItems 32 | ehthumbs.db 33 | Thumbs.db 34 | Dockerfile -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # [Choice] bionic (18.04), focal (20.04) 2 | ARG VARIANT="focal" 3 | FROM ubuntu:${VARIANT} 4 | 5 | # Restate the variant to use it later on in the llvm and cmake installations 6 | ARG VARIANT 7 | 8 | # Install necessary packages available from standard repos 9 | RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 10 | apt-get install -y --no-install-recommends \ 11 | software-properties-common wget apt-utils file zip \ 12 | openssh-client gpg-agent socat rsync \ 13 | make ninja-build git \ 14 | python3 python3-pip 15 | 16 | # Install conan 17 | RUN python3 -m pip install --upgrade pip setuptools && \ 18 | python3 -m pip install conan && \ 19 | conan --version 20 | 21 | # By default, anything you run in Docker is done as superuser. 22 | # Conan runs some install commands as superuser, and will prepend `sudo` to 23 | # these commands, unless `CONAN_SYSREQUIRES_SUDO=0` is in your env variables. 24 | ENV CONAN_SYSREQUIRES_SUDO 0 25 | # Some packages request that Conan use the system package manager to install 26 | # a few dependencies. This flag allows Conan to proceed with these installations; 27 | # leaving this flag undefined can cause some installation failures. 28 | ENV CONAN_SYSREQUIRES_MODE enabled 29 | 30 | # User-settable versions: 31 | # This Dockerfile should support gcc-[7, 8, 9, 10, 11] and clang-[10, 11, 12, 13] 32 | # Earlier versions of clang will require significant modifications to the IWYU section 33 | ARG GCC_VER="11" 34 | # Add gcc-${GCC_VER} 35 | RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test && \ 36 | apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 37 | apt-get install -y --no-install-recommends \ 38 | gcc-${GCC_VER} g++-${GCC_VER} gdb 39 | 40 | # Set gcc-${GCC_VER} as default gcc 41 | RUN update-alternatives --install /usr/bin/gcc gcc $(which gcc-${GCC_VER}) 100 42 | RUN update-alternatives --install /usr/bin/g++ g++ $(which g++-${GCC_VER}) 100 43 | 44 | ARG LLVM_VER="13" 45 | # Add clang-${LLVM_VER} 46 | ARG LLVM_URL="http://apt.llvm.org/${VARIANT}/" 47 | ARG LLVM_PKG="llvm-toolchain-${VARIANT}-${LLVM_VER}" 48 | RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - 2>/dev/null && \ 49 | add-apt-repository -y "deb ${LLVM_URL} ${LLVM_PKG} main" && \ 50 | apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 51 | apt-get install -y --no-install-recommends \ 52 | clang-${LLVM_VER} lldb-${LLVM_VER} lld-${LLVM_VER} clangd-${LLVM_VER} \ 53 | llvm-${LLVM_VER}-dev libclang-${LLVM_VER}-dev clang-tidy-${LLVM_VER} 54 | 55 | # Set the default clang-tidy, so CMake can find it 56 | RUN update-alternatives --install /usr/bin/clang-tidy clang-tidy $(which clang-tidy-${LLVM_VER}) 1 57 | 58 | # Set clang-${LLVM_VER} as default clang 59 | RUN update-alternatives --install /usr/bin/clang clang $(which clang-${LLVM_VER}) 100 60 | RUN update-alternatives --install /usr/bin/clang++ clang++ $(which clang++-${LLVM_VER}) 100 61 | 62 | # Add current cmake/ccmake, from Kitware 63 | ARG CMAKE_URL="https://apt.kitware.com/ubuntu/" 64 | ARG CMAKE_PKG=${VARIANT} 65 | RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null \ 66 | | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null && \ 67 | apt-add-repository -y "deb ${CMAKE_URL} ${CMAKE_PKG} main" && \ 68 | apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 69 | apt-get install -y --no-install-recommends cmake cmake-curses-gui 70 | 71 | # Install editors 72 | RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 73 | apt-get install -y --no-install-recommends \ 74 | neovim emacs nano 75 | 76 | # Install optional dependecies 77 | RUN apt-get update -qq && export DEBIAN_FRONTEND=noninteractive && \ 78 | apt-get install -y --no-install-recommends \ 79 | doxygen graphviz ccache cppcheck 80 | 81 | # Install include-what-you-use 82 | ENV IWYU /home/iwyu 83 | ENV IWYU_BUILD ${IWYU}/build 84 | ENV IWYU_SRC ${IWYU}/include-what-you-use 85 | RUN mkdir -p ${IWYU_BUILD} && \ 86 | git clone --branch clang_${LLVM_VER} \ 87 | https://github.com/include-what-you-use/include-what-you-use.git \ 88 | ${IWYU_SRC} 89 | RUN CC=clang-${LLVM_VER} CXX=clang++-${LLVM_VER} cmake -S ${IWYU_SRC} \ 90 | -B ${IWYU_BUILD} \ 91 | -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=/usr/lib/llvm-${LLVM_VER} && \ 92 | cmake --build ${IWYU_BUILD} -j && \ 93 | cmake --install ${IWYU_BUILD} 94 | 95 | # Per https://github.com/include-what-you-use/include-what-you-use#how-to-install: 96 | # `You need to copy the Clang include directory to the expected location before 97 | # running (similarly, use include-what-you-use -print-resource-dir to learn 98 | # exactly where IWYU wants the headers).` 99 | RUN mkdir -p $(include-what-you-use -print-resource-dir 2>/dev/null) 100 | RUN ln -s $(readlink -f /usr/lib/clang/${LLVM_VER}/include) \ 101 | $(include-what-you-use -print-resource-dir 2>/dev/null)/include 102 | 103 | ## Cleanup cached apt data we don't need anymore 104 | RUN apt-get autoremove -y && apt-get clean && \ 105 | rm -rf /var/lib/apt/lists/* 106 | 107 | # Allow the user to set compiler defaults 108 | ARG USE_CLANG 109 | # if --build-arg USE_CLANG=1, set CC to 'clang' or set to null otherwise. 110 | ENV CC=${USE_CLANG:+"clang"} 111 | ENV CXX=${USE_CLANG:+"clang++"} 112 | # if CC is null, set it to 'gcc' (or leave as is otherwise). 113 | ENV CC=${CC:-"gcc"} 114 | ENV CXX=${CXX:-"g++"} 115 | 116 | # Include project 117 | #ADD . /workspaces/cpp_starter_project 118 | #WORKDIR /workspaces/cpp_starter_project 119 | 120 | CMD ["/bin/bash"] 121 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.205.2/containers/cpp 3 | { 4 | "name": "C++", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Ubuntu OS version. Options: [bionic, focal]. Default: focal 8 | // Update 'GCC_VER' to pick a gcc and g++ version. Options: [7, 8, 9, 10, 11]. Default: 11 9 | // Update 'LLVM_VER' to pick clang version. Options: [10, 11, 12, 13]. Default: 13 10 | // Update 'USE_CLANG' to set clang as the default C and C++ compiler. Options: [1, null]. Default null 11 | // "args": { 12 | // "VARIANT": "focal", 13 | // "GCC_VER": "11", 14 | // "LLVM_VER": "13" 15 | // } 16 | }, 17 | "runArgs": [ 18 | "--cap-add=SYS_PTRACE", 19 | "--security-opt", 20 | "seccomp=unconfined" 21 | ], 22 | // Set *default* container specific settings.json values on container create. 23 | "settings": { 24 | "cmake.configureOnOpen": true, 25 | "editor.formatOnSave": true 26 | }, 27 | // Add the IDs of extensions you want installed when the container is created. 28 | "extensions": [ 29 | "ms-vscode.cpptools", 30 | "ms-vscode.cmake-tools", 31 | "twxs.cmake", 32 | "ms-vscode.cpptools-themes", 33 | "cschlosser.doxdocgen", 34 | "eamodio.gitlens", 35 | "ms-python.python", 36 | "ms-python.vscode-pylance", 37 | "mutantdino.resourcemonitor" 38 | ], 39 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 40 | // "forwardPorts": [], 41 | // Use 'postCreateCommand' to run commands after the container is created. 42 | //"postCreateCommand": "uname -a", 43 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 44 | //"remoteUser": "vscode", 45 | "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=delegated", 46 | "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", 47 | "features": { 48 | "git": "latest", 49 | "git-lfs": "latest", 50 | "powershell": "latest" 51 | } 52 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Git Line Endings # 3 | ############################### 4 | 5 | * text=auto eol=lf 6 | *.{cmd,[cC][mM][dD]} text eol=crlf 7 | *.{bat,[bB][aA][tT]} text eol=crlf 8 | *.{vcxproj,vcxproj.filters} text eol=crlf 9 | 10 | ############################### 11 | # Git Large File System (LFS) # 12 | ############################### 13 | 14 | # Archives 15 | #*.7z filter=lfs diff=lfs merge=lfs -text 16 | #*.br filter=lfs diff=lfs merge=lfs -text 17 | #*.gz filter=lfs diff=lfs merge=lfs -text 18 | #*.tar filter=lfs diff=lfs merge=lfs -text 19 | #*.zip filter=lfs diff=lfs merge=lfs -text 20 | 21 | # Documents 22 | #*.pdf filter=lfs diff=lfs merge=lfs -text 23 | 24 | # Images 25 | #*.gif filter=lfs diff=lfs merge=lfs -text 26 | #*.ico filter=lfs diff=lfs merge=lfs -text 27 | #*.jpg filter=lfs diff=lfs merge=lfs -text 28 | #*.pdf filter=lfs diff=lfs merge=lfs -text 29 | #*.png filter=lfs diff=lfs merge=lfs -text 30 | #*.psd filter=lfs diff=lfs merge=lfs -text 31 | #*.webp filter=lfs diff=lfs merge=lfs -text 32 | 33 | # Fonts 34 | #*.woff2 filter=lfs diff=lfs merge=lfs -text 35 | 36 | # Other 37 | #*.exe filter=lfs diff=lfs merge=lfs -text -------------------------------------------------------------------------------- /.github/actions/setup_cache/action.yml: -------------------------------------------------------------------------------- 1 | 2 | name: 'setup_cache' 3 | description: 'sets up the shared cache' 4 | inputs: 5 | compiler: 6 | required: true 7 | type: string 8 | build_type: 9 | required: true 10 | type: string 11 | generator: 12 | required: true 13 | type: string 14 | packaging_maintainer_mode: 15 | required: true 16 | type: string 17 | 18 | 19 | runs: 20 | using: "composite" 21 | steps: 22 | - name: Cache 23 | uses: actions/cache@v3 24 | with: 25 | # You might want to add .ccache to your cache configuration? 26 | path: | 27 | ~/.cache/pip 28 | ~/.ccache 29 | key: ${{ runner.os }}-${{ inputs.compiler }}-${{ inputs.build_type }}-${{ inputs.generator }}-${{ inputs.packaging_maintainer_mode }}-${{ hashFiles('**/CMakeLists.txt') }} 30 | restore-keys: | 31 | ${{ runner.os }}-${{ inputs.compiler }}-${{ inputs.build_type }} 32 | 33 | -------------------------------------------------------------------------------- /.github/constants.env: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=chains 2 | -------------------------------------------------------------------------------- /.github/workflows/auto-clang-format.yml: -------------------------------------------------------------------------------- 1 | name: auto-clang-format 2 | on: [pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - uses: actions/checkout@v3 10 | - uses: DoozyX/clang-format-lint-action@v0.13 11 | with: 12 | source: '.' 13 | exclude: './third_party ./external' 14 | extensions: 'h,cpp,hpp' 15 | clangFormatVersion: 12 16 | inplace: True 17 | - uses: EndBug/add-and-commit@v4 18 | with: 19 | author_name: Clang Robot 20 | author_email: robot@example.com 21 | message: ':art: Committing clang-format changes' 22 | env: 23 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | pull_request: 4 | release: 5 | types: [published] 6 | push: 7 | tags: 8 | branches: 9 | - main 10 | - develop 11 | 12 | env: 13 | CLANG_TIDY_VERSION: "15.0.2" 14 | VERBOSE: 1 15 | 16 | 17 | jobs: 18 | Test: 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | 23 | # Recommendations: 24 | # * support at least 2 operating systems 25 | # * support at least 2 compilers 26 | # * make sure all supported configurations for your project are built 27 | # 28 | # Disable/enable builds in this list to meet the above recommendations 29 | # and your own projects needs 30 | matrix: 31 | os: 32 | - ubuntu-20.04 33 | - macos-10.15 34 | - windows-2019 35 | compiler: 36 | # you can specify the version after `-` like "llvm-15.0.2". 37 | - llvm-15.0.2 38 | - gcc-11 39 | generator: 40 | - "Ninja Multi-Config" 41 | build_type: 42 | - Release 43 | - Debug 44 | packaging_maintainer_mode: 45 | - ON 46 | - OFF 47 | build_shared: 48 | - OFF 49 | 50 | exclude: 51 | # mingw is determined by this author to be too buggy to support 52 | - os: windows-2019 53 | compiler: gcc-11 54 | 55 | include: 56 | # Add appropriate variables for gcov version required. This will intentionally break 57 | # if you try to use a compiler that does not have gcov set 58 | - compiler: gcc-11 59 | gcov_executable: gcov 60 | enable_ipo: On 61 | 62 | - compiler: llvm-15.0.2 63 | enable_ipo: Off 64 | gcov_executable: "llvm-cov gcov" 65 | 66 | - os: macos-10.15 67 | enable_ipo: Off 68 | 69 | # Set up preferred package generators, for given build configurations 70 | - build_type: Release 71 | packaging_maintainer_mode: OFF 72 | package_generator: TBZ2 73 | 74 | # This exists solely to make sure a non-multiconfig build works 75 | - os: ubuntu-20.04 76 | compiler: gcc-11 77 | generator: "Unix Makefiles" 78 | build_type: Debug 79 | gcov_executable: gcov 80 | packaging_maintainer_mode: On 81 | enable_ipo: Off 82 | 83 | # Windows msvc builds 84 | - os: windows-2022 85 | compiler: msvc 86 | generator: "Visual Studio 17 2022" 87 | build_type: Debug 88 | packaging_maintainer_mode: On 89 | enable_ipo: On 90 | 91 | - os: windows-2022 92 | compiler: msvc 93 | generator: "Visual Studio 17 2022" 94 | build_type: Release 95 | packaging_maintainer_mode: On 96 | enable_ipo: On 97 | 98 | - os: windows-2022 99 | compiler: msvc 100 | generator: "Visual Studio 17 2022" 101 | build_type: Debug 102 | packaging_maintainer_mode: Off 103 | 104 | - os: windows-2022 105 | compiler: msvc 106 | generator: "Visual Studio 17 2022" 107 | build_type: Release 108 | packaging_maintainer_mode: Off 109 | package_generator: ZIP 110 | 111 | - os: windows-2022 112 | compiler: msvc 113 | generator: "Visual Studio 17 2022" 114 | build_type: Release 115 | packaging_maintainer_mode: On 116 | enable_ipo: On 117 | build_shared: On 118 | 119 | 120 | steps: 121 | - name: Check for llvm version mismatches 122 | if: ${{ contains(matrix.compiler, 'llvm') && !contains(matrix.compiler, env.CLANG_TIDY_VERSION) }} 123 | uses: actions/github-script@v3 124 | with: 125 | script: | 126 | core.setFailed('There is a mismatch between configured llvm compiler and clang-tidy version chosen') 127 | 128 | - uses: actions/checkout@v3 129 | 130 | - name: Setup Cache 131 | uses: ./.github/actions/setup_cache 132 | with: 133 | compiler: ${{ matrix.compiler }} 134 | build_type: ${{ matrix.build_type }} 135 | packaging_maintainer_mode: ${{ matrix.packaging_maintainer_mode }} 136 | generator: ${{ matrix.generator }} 137 | 138 | - name: Project Name 139 | uses: cardinalby/export-env-action@v2 140 | with: 141 | envFile: '.github/constants.env' 142 | 143 | 144 | - name: Setup Cpp 145 | uses: aminya/setup-cpp@v1 146 | with: 147 | compiler: ${{ matrix.compiler }} 148 | vcvarsall: ${{ contains(matrix.os, 'windows' )}} 149 | 150 | cmake: true 151 | ninja: true 152 | vcpkg: false 153 | ccache: true 154 | clangtidy: ${{ env.CLANG_TIDY_VERSION }} 155 | 156 | 157 | cppcheck: true 158 | 159 | gcovr: true 160 | opencppcoverage: true 161 | 162 | - name: Configure CMake 163 | run: | 164 | cmake -S . -B ./build -G "${{matrix.generator}}" -D${{ env.PROJECT_NAME }}_ENABLE_IPO=${{matrix.enable_ipo }} -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} 165 | 166 | - name: Build 167 | # Execute the build. You can specify a specific target with "--target " 168 | run: | 169 | cmake --build ./build --config ${{matrix.build_type}} 170 | 171 | - name: Unix - Test and coverage 172 | if: runner.os != 'Windows' 173 | working-directory: ./build 174 | # Execute tests defined by the CMake configuration. 175 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 176 | run: | 177 | ctest -C ${{matrix.build_type}} 178 | gcovr -j ${{env.nproc}} --delete --root ../ --print-summary --xml-pretty --xml coverage.xml . --gcov-executable '${{ matrix.gcov_executable }}' 179 | 180 | - name: Windows - Test and coverage 181 | if: runner.os == 'Windows' 182 | working-directory: ./build 183 | run: | 184 | OpenCppCoverage.exe --export_type cobertura:coverage.xml --cover_children -- ctest -C ${{matrix.build_type}} 185 | 186 | - name: CPack 187 | if: matrix.package_generator != '' 188 | working-directory: ./build 189 | run: | 190 | cpack -C ${{matrix.build_type}} -G ${{matrix.package_generator}} 191 | 192 | - name: Publish Tagged Release 193 | uses: softprops/action-gh-release@v1 194 | if: ${{ startsWith(github.ref, 'refs/tags/') && matrix.package_generator != '' }} 195 | with: 196 | files: | 197 | build/*-*${{ matrix.build_type }}*-*.* 198 | 199 | 200 | - name: Publish to codecov 201 | uses: codecov/codecov-action@v2 202 | with: 203 | flags: ${{ runner.os }} 204 | name: ${{ runner.os }}-coverage 205 | files: ./build/coverage.xml 206 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main, develop ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main, develop ] 20 | schedule: 21 | - cron: '38 0 * * 5' 22 | 23 | 24 | jobs: 25 | analyze: 26 | name: Analyze 27 | runs-on: ubuntu-latest 28 | permissions: 29 | actions: read 30 | contents: read 31 | security-events: write 32 | 33 | strategy: 34 | fail-fast: false 35 | matrix: 36 | language: [ 'cpp' ] 37 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 38 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 39 | compiler: 40 | # you can specify the version after `-` like "llvm-13.0.0". 41 | - gcc-11 42 | generator: 43 | - "Ninja Multi-Config" 44 | build_type: 45 | - Debug 46 | packaging_maintainer_mode: 47 | - ON 48 | 49 | 50 | steps: 51 | - uses: actions/checkout@v3 52 | 53 | - name: Setup Cache 54 | uses: ./.github/actions/setup_cache 55 | with: 56 | compiler: ${{ matrix.compiler }} 57 | build_type: ${{ matrix.build_type }} 58 | packaging_maintainer_mode: ${{ matrix.packaging_maintainer_mode }} 59 | generator: ${{ matrix.generator }} 60 | 61 | - name: Project Name 62 | uses: cardinalby/export-env-action@v2 63 | with: 64 | envFile: '.github/constants.env' 65 | 66 | 67 | - name: Setup Cpp 68 | uses: aminya/setup-cpp@v1 69 | with: 70 | compiler: ${{ matrix.compiler }} 71 | vcvarsall: ${{ contains(matrix.os, 'windows' )}} 72 | 73 | cmake: true 74 | ninja: true 75 | vcpkg: false 76 | ccache: true 77 | clangtidy: false 78 | 79 | cppcheck: false 80 | 81 | gcovr: false 82 | opencppcoverage: false 83 | 84 | # make sure coverage is only enabled for Debug builds, since it sets -O0 to make sure coverage 85 | # has meaningful results 86 | - name: Configure CMake 87 | run: | 88 | cmake -S . -B ./build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} 89 | 90 | # Initializes the CodeQL tools for scanning. 91 | - name: Initialize CodeQL 92 | uses: github/codeql-action/init@v2 93 | with: 94 | languages: ${{ matrix.language }} 95 | # If you wish to specify custom queries, you can do so here or in a config file. 96 | # By default, queries listed here will override any specified in a config file. 97 | # Prefix the list here with "+" to use these queries and those in the config file. 98 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 99 | 100 | 101 | - name: Build 102 | # Execute the build. You can specify a specific target with "--target " 103 | run: | 104 | cmake --build ./build --config ${{matrix.build_type}} 105 | 106 | - name: Perform CodeQL Analysis 107 | uses: github/codeql-action/analyze@v2 108 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories and binary files 2 | build/ 3 | out/ 4 | cmake-build-*/ 5 | conan-cache/ 6 | 7 | # User spesific settings 8 | CMakeUserPresets.json 9 | 10 | # IDE files 11 | .vs/ 12 | .idea/ 13 | .vscode/ 14 | !.vscode/settings.json 15 | !.vscode/tasks.json 16 | !.vscode/launch.json 17 | !.vscode/extensions.json 18 | *.swp 19 | *~ 20 | _ReSharper* 21 | *.log 22 | 23 | # OS Generated Files 24 | .DS_Store 25 | .AppleDouble 26 | .LSOverride 27 | ._* 28 | .Spotlight-V100 29 | .Trashes 30 | .Trash-* 31 | $RECYCLE.BIN/ 32 | .TemporaryItems 33 | ehthumbs.db 34 | Thumbs.db 35 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: ubuntu:latest 2 | 3 | stages: 4 | - test 5 | 6 | .setup_linux: &setup_linux | 7 | DEBIAN_FRONTEND=noninteractive 8 | 9 | # set time-zone 10 | TZ=Canada/Pacific 11 | ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 12 | 13 | # for downloading 14 | apt-get update -qq 15 | apt-get install -y --no-install-recommends curl gnupg ca-certificates 16 | 17 | # keys used by apt 18 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 3B4FE6ACC0B21F32 19 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 40976EAF437D05B5 20 | apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F 21 | 22 | .setup_cpp: &setup_cpp | 23 | curl -LJO "https://github.com/aminya/setup-cpp/releases/download/v0.10.0/setup_cpp_linux" 24 | chmod +x setup_cpp_linux 25 | ./setup_cpp_linux --compiler $compiler --cmake true --ninja true --conan true --ccache true --clangtidy true --clangformat true --cppcheck true 26 | source ~/.profile 27 | 28 | .test: &test | 29 | # Build and Test 30 | cmake -S . -B ./build -DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo 31 | cmake --build ./build --config RelWithDebInfo 32 | 33 | test_linux_llvm: 34 | stage: test 35 | variables: 36 | compiler: llvm 37 | script: 38 | - *setup_linux 39 | - *setup_cpp 40 | - *test 41 | 42 | test_linux_gcc: 43 | stage: test 44 | variables: 45 | compiler: gcc 46 | script: 47 | - *setup_linux 48 | - *setup_cpp 49 | - *test 50 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21) 2 | 3 | # This template attempts to be "fetch_content"-able 4 | # so that it works well with tools like CPM or other 5 | # manual dependency management 6 | 7 | # Only set the cxx_standard if it is not set by someone else 8 | if (NOT DEFINED CMAKE_CXX_STANDARD) 9 | set(CMAKE_CXX_STANDARD 20) 10 | endif() 11 | 12 | # strongly encouraged to enable this globally to avoid conflicts between 13 | # -Wpedantic being enabled and -std=c++20 and -std=gnu++20 for example 14 | # when compiling with PCH enabled 15 | set(CMAKE_CXX_EXTENSIONS OFF) 16 | 17 | # Set the project name and language 18 | project( 19 | chains 20 | VERSION 0.0.1 21 | DESCRIPTION "" 22 | HOMEPAGE_URL "https://github.com/stlab/chains" 23 | LANGUAGES CXX C) 24 | 25 | include(cmake/PreventInSourceBuilds.cmake) 26 | include(ProjectOptions.cmake) 27 | 28 | 29 | chains_setup_options() 30 | 31 | chains_global_options() 32 | include(Dependencies.cmake) 33 | chains_setup_dependencies() 34 | 35 | chains_local_options() 36 | 37 | # don't know if this should be set globally from here or not... 38 | set(CMAKE_CXX_VISIBILITY_PRESET hidden) 39 | 40 | set(GIT_SHA 41 | "Unknown" 42 | CACHE STRING "SHA this build was generated from") 43 | string( 44 | SUBSTRING "${GIT_SHA}" 45 | 0 46 | 8 47 | GIT_SHORT_SHA) 48 | 49 | target_compile_features(chains_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) 50 | 51 | add_library(chains::chains_options ALIAS chains_options) 52 | add_library(chains::chains_warnings ALIAS chains_warnings) 53 | 54 | #add_library(chains::chains_options INTERFACE IMPORTED) 55 | #add_library(chains::chains_warnings INTERFACE IMPORTED) 56 | 57 | # configure files based on CMake configuration options 58 | add_subdirectory(configured_files) 59 | 60 | # Adding the src: 61 | add_subdirectory(src) 62 | 63 | # Don't even look at tests if we're not top level 64 | if(NOT PROJECT_IS_TOP_LEVEL) 65 | return() 66 | endif() 67 | 68 | # Adding the tests: 69 | include(CTest) 70 | 71 | if(BUILD_TESTING) 72 | message(AUTHOR_WARNING "Building Tests. Be sure to check out test/constexpr_tests.cpp for constexpr testing") 73 | add_subdirectory(test) 74 | endif() 75 | 76 | 77 | if(chains_BUILD_FUZZ_TESTS) 78 | message(AUTHOR_WARNING "Building Fuzz Tests, using fuzzing sanitizer https://www.llvm.org/docs/LibFuzzer.html") 79 | if (NOT chains_ENABLE_ADDRESS_SANITIZER AND NOT chains_ENABLE_THREAD_SANITIZER) 80 | message(WARNING "You need asan or tsan enabled for meaningful fuzz testing") 81 | endif() 82 | add_subdirectory(fuzz_test) 83 | 84 | endif() 85 | 86 | # If MSVC is being used, and ASAN is enabled, we need to set the debugger environment 87 | # so that it behaves well with MSVC's debugger, and we can run the target from visual studio 88 | if(MSVC) 89 | get_all_installable_targets(all_targets) 90 | message("all_targets=${all_targets}") 91 | set_target_properties(${all_targets} PROPERTIES VS_DEBUGGER_ENVIRONMENT "PATH=$(VC_ExecutablePath_x64);%PATH%") 92 | endif() 93 | 94 | # set the startup project for the "play" button in MSVC 95 | set_property(DIRECTORY PROPERTY VS_STARTUP_PROJECT intro) 96 | 97 | if(CMAKE_SKIP_INSTALL_RULES) 98 | return() 99 | endif() 100 | 101 | include(cmake/PackageProject.cmake) 102 | 103 | # Add other targets that you want installed here, by default we just package the one executable 104 | # we know we want to ship 105 | chains_package_project( 106 | TARGETS 107 | intro 108 | chains_options 109 | chains_warnings 110 | # FIXME: this does not work! CK 111 | # PRIVATE_DEPENDENCIES_CONFIGURED project_options project_warnings 112 | ) 113 | 114 | # Experience shows that explicit package naming can help make it easier to sort 115 | # out potential ABI related issues before they start, while helping you 116 | # track a build to a specific GIT SHA 117 | set(CPACK_PACKAGE_FILE_NAME 118 | "${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${GIT_SHORT_SHA}-${CMAKE_SYSTEM_NAME}-${CMAKE_BUILD_TYPE}-${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}" 119 | ) 120 | 121 | include(CPack) 122 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 21, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "conf-common", 11 | "description": "General settings that apply to all configurations", 12 | "hidden": true, 13 | "generator": "Ninja", 14 | "binaryDir": "${sourceDir}/out/build/${presetName}", 15 | "installDir": "${sourceDir}/out/install/${presetName}" 16 | }, 17 | { 18 | "name": "conf-windows-common", 19 | "description": "Windows settings for MSBuild toolchain that apply to msvc and clang", 20 | "hidden": true, 21 | "inherits": "conf-common", 22 | "condition": { 23 | "type": "equals", 24 | "lhs": "${hostSystemName}", 25 | "rhs": "Windows" 26 | }, 27 | "architecture": { 28 | "value": "x64", 29 | "strategy": "external" 30 | }, 31 | "toolset": { 32 | "value": "host=x64", 33 | "strategy": "external" 34 | }, 35 | "cacheVariables": { 36 | "ENABLE_CPPCHECK_DEFAULT": "FALSE", 37 | "ENABLE_CLANG_TIDY_DEFAULT": "FALSE" 38 | } 39 | }, 40 | { 41 | "name": "conf-unixlike-common", 42 | "description": "Unix-like OS settings for gcc and clang toolchains", 43 | "hidden": true, 44 | "inherits": "conf-common", 45 | "condition": { 46 | "type": "inList", 47 | "string": "${hostSystemName}", 48 | "list": [ 49 | "Linux", 50 | "Darwin" 51 | ] 52 | }, 53 | "vendor": { 54 | "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { 55 | "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" 56 | } 57 | } 58 | }, 59 | { 60 | "name": "windows-msvc-debug-developer-mode", 61 | "displayName": "msvc Debug (Developer Mode)", 62 | "description": "Target Windows with the msvc compiler, debug build type", 63 | "inherits": "conf-windows-common", 64 | "cacheVariables": { 65 | "CMAKE_C_COMPILER": "cl", 66 | "CMAKE_CXX_COMPILER": "cl", 67 | "CMAKE_BUILD_TYPE": "Debug", 68 | "ENABLE_DEVELOPER_MODE": "ON" 69 | } 70 | }, 71 | { 72 | "name": "windows-msvc-release-developer-mode", 73 | "displayName": "msvc Release (Developer Mode)", 74 | "description": "Target Windows with the msvc compiler, release build type", 75 | "inherits": "conf-windows-common", 76 | "cacheVariables": { 77 | "CMAKE_C_COMPILER": "cl", 78 | "CMAKE_CXX_COMPILER": "cl", 79 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 80 | "ENABLE_DEVELOPER_MODE": "ON" 81 | } 82 | }, 83 | { 84 | "name": "windows-msvc-debug-user-mode", 85 | "displayName": "msvc Debug (User Mode)", 86 | "description": "Target Windows with the msvc compiler, debug build type", 87 | "inherits": "conf-windows-common", 88 | "cacheVariables": { 89 | "CMAKE_C_COMPILER": "cl", 90 | "CMAKE_CXX_COMPILER": "cl", 91 | "CMAKE_BUILD_TYPE": "Debug", 92 | "ENABLE_DEVELOPER_MODE": "OFF" 93 | } 94 | }, 95 | { 96 | "name": "windows-msvc-release-user-mode", 97 | "displayName": "msvc Release (User Mode)", 98 | "description": "Target Windows with the msvc compiler, release build type", 99 | "inherits": "conf-windows-common", 100 | "cacheVariables": { 101 | "CMAKE_C_COMPILER": "cl", 102 | "CMAKE_CXX_COMPILER": "cl", 103 | "CMAKE_BUILD_TYPE": "RelWithDebInfo", 104 | "ENABLE_DEVELOPER_MODE": "OFF" 105 | } 106 | }, 107 | { 108 | "name": "windows-clang-debug", 109 | "displayName": "clang Debug", 110 | "description": "Target Windows with the clang compiler, debug build type", 111 | "inherits": "conf-windows-common", 112 | "cacheVariables": { 113 | "CMAKE_C_COMPILER": "clang-cl", 114 | "CMAKE_CXX_COMPILER": "clang-cl", 115 | "CMAKE_BUILD_TYPE": "Debug" 116 | }, 117 | "vendor": { 118 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 119 | "intelliSenseMode": "windows-clang-x64" 120 | } 121 | } 122 | }, 123 | { 124 | "name": "windows-clang-release", 125 | "displayName": "clang Release", 126 | "description": "Target Windows with the clang compiler, release build type", 127 | "inherits": "conf-windows-common", 128 | "cacheVariables": { 129 | "CMAKE_C_COMPILER": "clang-cl", 130 | "CMAKE_CXX_COMPILER": "clang-cl", 131 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 132 | }, 133 | "vendor": { 134 | "microsoft.com/VisualStudioSettings/CMake/1.0": { 135 | "intelliSenseMode": "windows-clang-x64" 136 | } 137 | } 138 | }, 139 | { 140 | "name": "unixlike-gcc-debug", 141 | "displayName": "gcc Debug", 142 | "description": "Target Unix-like OS with the gcc compiler, debug build type", 143 | "inherits": "conf-unixlike-common", 144 | "cacheVariables": { 145 | "CMAKE_C_COMPILER": "gcc", 146 | "CMAKE_CXX_COMPILER": "g++", 147 | "CMAKE_BUILD_TYPE": "Debug" 148 | } 149 | }, 150 | { 151 | "name": "unixlike-gcc-release", 152 | "displayName": "gcc Release", 153 | "description": "Target Unix-like OS with the gcc compiler, release build type", 154 | "inherits": "conf-unixlike-common", 155 | "cacheVariables": { 156 | "CMAKE_C_COMPILER": "gcc", 157 | "CMAKE_CXX_COMPILER": "g++", 158 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 159 | } 160 | }, 161 | { 162 | "name": "unixlike-clang-debug", 163 | "displayName": "clang Debug", 164 | "description": "Target Unix-like OS with the clang compiler, debug build type", 165 | "inherits": "conf-unixlike-common", 166 | "cacheVariables": { 167 | "CMAKE_C_COMPILER": "clang", 168 | "CMAKE_CXX_COMPILER": "clang++", 169 | "CMAKE_BUILD_TYPE": "Debug" 170 | } 171 | }, 172 | { 173 | "name": "unixlike-clang-release", 174 | "displayName": "clang Release", 175 | "description": "Target Unix-like OS with the clang compiler, release build type", 176 | "inherits": "conf-unixlike-common", 177 | "cacheVariables": { 178 | "CMAKE_C_COMPILER": "clang", 179 | "CMAKE_CXX_COMPILER": "clang++", 180 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 181 | } 182 | } 183 | ], 184 | "testPresets": [ 185 | { 186 | "name": "test-common", 187 | "description": "Test CMake settings that apply to all configurations", 188 | "hidden": true, 189 | "output": { 190 | "outputOnFailure": true 191 | }, 192 | "execution": { 193 | "noTestsAction": "error", 194 | "stopOnFailure": true 195 | } 196 | }, 197 | { 198 | "name": "test-windows-msvc-debug-developer-mode", 199 | "displayName": "Strict", 200 | "description": "Enable output and stop on failure", 201 | "inherits": "test-common", 202 | "configurePreset": "windows-msvc-debug-developer-mode" 203 | }, 204 | { 205 | "name": "test-windows-msvc-release-developer-mode", 206 | "displayName": "Strict", 207 | "description": "Enable output and stop on failure", 208 | "inherits": "test-common", 209 | "configurePreset": "windows-msvc-release-developer-mode" 210 | }, 211 | { 212 | "name": "test-windows-clang-debug", 213 | "displayName": "Strict", 214 | "description": "Enable output and stop on failure", 215 | "inherits": "test-common", 216 | "configurePreset": "windows-clang-debug" 217 | }, 218 | { 219 | "name": "test-windows-clang-release", 220 | "displayName": "Strict", 221 | "description": "Enable output and stop on failure", 222 | "inherits": "test-common", 223 | "configurePreset": "windows-clang-release" 224 | }, 225 | { 226 | "name": "test-unixlike-gcc-debug", 227 | "displayName": "Strict", 228 | "description": "Enable output and stop on failure", 229 | "inherits": "test-common", 230 | "configurePreset": "unixlike-gcc-debug" 231 | }, 232 | { 233 | "name": "test-unixlike-gcc-release", 234 | "displayName": "Strict", 235 | "description": "Enable output and stop on failure", 236 | "inherits": "test-common", 237 | "configurePreset": "unixlike-gcc-release" 238 | }, 239 | { 240 | "name": "test-unixlike-clang-debug", 241 | "displayName": "Strict", 242 | "description": "Enable output and stop on failure", 243 | "inherits": "test-common", 244 | "configurePreset": "unixlike-clang-debug" 245 | }, 246 | { 247 | "name": "test-unixlike-clang-release", 248 | "displayName": "Strict", 249 | "description": "Enable output and stop on failure", 250 | "inherits": "test-common", 251 | "configurePreset": "unixlike-clang-release" 252 | } 253 | ] 254 | } -------------------------------------------------------------------------------- /Dependencies.cmake: -------------------------------------------------------------------------------- 1 | include(cmake/CPM.cmake) 2 | 3 | # Done as a function so that updates to variables like 4 | # CMAKE_CXX_FLAGS don't propagate out to other 5 | # targets 6 | function(chains_setup_dependencies) 7 | 8 | # For each dependency, see if it's 9 | # already been provided to us by a parent project 10 | 11 | if(NOT TARGET stlab::stlab) 12 | cpmaddpackage( 13 | NAME stlab 14 | VERSION 2.0.0a3 15 | GITHUB_REPOSITORY "stlab/libraries" 16 | OPTIONS "BUILD_TESTING OFF") 17 | endif() 18 | 19 | if(NOT TARGET fmtlib::fmtlib) 20 | cpmaddpackage("gh:fmtlib/fmt#9.1.0") 21 | endif() 22 | 23 | if(NOT TARGET spdlog::spdlog) 24 | cpmaddpackage( 25 | NAME 26 | spdlog 27 | VERSION 28 | 1.11.0 29 | GITHUB_REPOSITORY 30 | "gabime/spdlog" 31 | OPTIONS 32 | "SPDLOG_FMT_EXTERNAL ON") 33 | endif() 34 | 35 | if(NOT TARGET Catch2::Catch2WithMain) 36 | cpmaddpackage("gh:catchorg/Catch2@3.3.2") 37 | endif() 38 | 39 | if(NOT TARGET CLI11::CLI11) 40 | cpmaddpackage("gh:CLIUtils/CLI11@2.3.2") 41 | endif() 42 | 43 | if(NOT TARGET ftxui::screen) 44 | cpmaddpackage("gh:ArthurSonzogni/FTXUI#e23dbc7473654024852ede60e2121276c5aab660") 45 | endif() 46 | 47 | if(NOT TARGET tools::tools) 48 | cpmaddpackage("gh:lefticus/tools#update_build_system") 49 | endif() 50 | 51 | endfunction() 52 | -------------------------------------------------------------------------------- /ProjectOptions.cmake: -------------------------------------------------------------------------------- 1 | include(cmake/SystemLink.cmake) 2 | include(cmake/LibFuzzer.cmake) 3 | include(CMakeDependentOption) 4 | include(CheckCXXCompilerFlag) 5 | 6 | 7 | macro(chains_supports_sanitizers) 8 | if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND NOT WIN32) 9 | set(SUPPORTS_UBSAN ON) 10 | else() 11 | set(SUPPORTS_UBSAN OFF) 12 | endif() 13 | 14 | if((CMAKE_CXX_COMPILER_ID MATCHES ".*Clang.*" OR CMAKE_CXX_COMPILER_ID MATCHES ".*GNU.*") AND WIN32) 15 | set(SUPPORTS_ASAN OFF) 16 | else() 17 | set(SUPPORTS_ASAN ON) 18 | endif() 19 | endmacro() 20 | 21 | macro(chains_setup_options) 22 | option(chains_ENABLE_HARDENING "Enable hardening" ON) 23 | option(chains_ENABLE_COVERAGE "Enable coverage reporting" OFF) 24 | cmake_dependent_option( 25 | chains_ENABLE_GLOBAL_HARDENING 26 | "Attempt to push hardening options to built dependencies" 27 | ON 28 | chains_ENABLE_HARDENING 29 | OFF) 30 | 31 | chains_supports_sanitizers() 32 | 33 | if(NOT PROJECT_IS_TOP_LEVEL OR chains_PACKAGING_MAINTAINER_MODE) 34 | option(chains_ENABLE_IPO "Enable IPO/LTO" OFF) 35 | option(chains_WARNINGS_AS_ERRORS "Treat Warnings As Errors" OFF) 36 | option(chains_ENABLE_USER_LINKER "Enable user-selected linker" OFF) 37 | option(chains_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" OFF) 38 | option(chains_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) 39 | option(chains_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" OFF) 40 | option(chains_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) 41 | option(chains_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) 42 | option(chains_ENABLE_UNITY_BUILD "Enable unity builds" OFF) 43 | option(chains_ENABLE_CLANG_TIDY "Enable clang-tidy" OFF) 44 | option(chains_ENABLE_CPPCHECK "Enable cpp-check analysis" OFF) 45 | option(chains_ENABLE_PCH "Enable precompiled headers" OFF) 46 | option(chains_ENABLE_CACHE "Enable ccache" OFF) 47 | else() 48 | option(chains_ENABLE_IPO "Enable IPO/LTO" ON) 49 | option(chains_WARNINGS_AS_ERRORS "Treat Warnings As Errors" ON) 50 | option(chains_ENABLE_USER_LINKER "Enable user-selected linker" OFF) 51 | option(chains_ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" ${SUPPORTS_ASAN}) 52 | option(chains_ENABLE_SANITIZER_LEAK "Enable leak sanitizer" OFF) 53 | option(chains_ENABLE_SANITIZER_UNDEFINED "Enable undefined sanitizer" ${SUPPORTS_UBSAN}) 54 | option(chains_ENABLE_SANITIZER_THREAD "Enable thread sanitizer" OFF) 55 | option(chains_ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" OFF) 56 | option(chains_ENABLE_UNITY_BUILD "Enable unity builds" OFF) 57 | option(chains_ENABLE_CLANG_TIDY "Enable clang-tidy" ON) 58 | option(chains_ENABLE_CPPCHECK "Enable cpp-check analysis" ON) 59 | option(chains_ENABLE_PCH "Enable precompiled headers" OFF) 60 | option(chains_ENABLE_CACHE "Enable ccache" ON) 61 | endif() 62 | 63 | if(NOT PROJECT_IS_TOP_LEVEL) 64 | mark_as_advanced( 65 | chains_ENABLE_IPO 66 | chains_WARNINGS_AS_ERRORS 67 | chains_ENABLE_USER_LINKER 68 | chains_ENABLE_SANITIZER_ADDRESS 69 | chains_ENABLE_SANITIZER_LEAK 70 | chains_ENABLE_SANITIZER_UNDEFINED 71 | chains_ENABLE_SANITIZER_THREAD 72 | chains_ENABLE_SANITIZER_MEMORY 73 | chains_ENABLE_UNITY_BUILD 74 | chains_ENABLE_CLANG_TIDY 75 | chains_ENABLE_CPPCHECK 76 | chains_ENABLE_COVERAGE 77 | chains_ENABLE_PCH 78 | chains_ENABLE_CACHE) 79 | endif() 80 | 81 | chains_check_libfuzzer_support(LIBFUZZER_SUPPORTED) 82 | if(LIBFUZZER_SUPPORTED AND (chains_ENABLE_SANITIZER_ADDRESS OR chains_ENABLE_SANITIZER_THREAD OR chains_ENABLE_SANITIZER_UNDEFINED)) 83 | set(DEFAULT_FUZZER ON) 84 | else() 85 | set(DEFAULT_FUZZER OFF) 86 | endif() 87 | 88 | option(chains_BUILD_FUZZ_TESTS "Enable fuzz testing executable" ${DEFAULT_FUZZER}) 89 | 90 | endmacro() 91 | 92 | macro(chains_global_options) 93 | if(chains_ENABLE_IPO) 94 | include(cmake/InterproceduralOptimization.cmake) 95 | chains_enable_ipo() 96 | endif() 97 | 98 | chains_supports_sanitizers() 99 | 100 | if(chains_ENABLE_HARDENING AND chains_ENABLE_GLOBAL_HARDENING) 101 | include(cmake/Hardening.cmake) 102 | if(NOT SUPPORTS_UBSAN 103 | OR chains_ENABLE_SANITIZER_UNDEFINED 104 | OR chains_ENABLE_SANITIZER_ADDRESS 105 | OR chains_ENABLE_SANITIZER_THREAD 106 | OR chains_ENABLE_SANITIZER_LEAK) 107 | set(ENABLE_UBSAN_MINIMAL_RUNTIME FALSE) 108 | else() 109 | set(ENABLE_UBSAN_MINIMAL_RUNTIME TRUE) 110 | endif() 111 | message("${chains_ENABLE_HARDENING} ${ENABLE_UBSAN_MINIMAL_RUNTIME} ${chains_ENABLE_SANITIZER_UNDEFINED}") 112 | chains_enable_hardening(chains_options ON ${ENABLE_UBSAN_MINIMAL_RUNTIME}) 113 | endif() 114 | endmacro() 115 | 116 | macro(chains_local_options) 117 | if(PROJECT_IS_TOP_LEVEL) 118 | include(cmake/StandardProjectSettings.cmake) 119 | endif() 120 | 121 | add_library(chains_warnings INTERFACE) 122 | add_library(chains_options INTERFACE) 123 | 124 | include(cmake/CompilerWarnings.cmake) 125 | chains_set_project_warnings( 126 | chains_warnings 127 | ${chains_WARNINGS_AS_ERRORS} 128 | "" 129 | "" 130 | "" 131 | "") 132 | 133 | if(chains_ENABLE_USER_LINKER) 134 | include(cmake/Linker.cmake) 135 | configure_linker(chains_options) 136 | endif() 137 | 138 | include(cmake/Sanitizers.cmake) 139 | chains_enable_sanitizers( 140 | chains_options 141 | ${chains_ENABLE_SANITIZER_ADDRESS} 142 | ${chains_ENABLE_SANITIZER_LEAK} 143 | ${chains_ENABLE_SANITIZER_UNDEFINED} 144 | ${chains_ENABLE_SANITIZER_THREAD} 145 | ${chains_ENABLE_SANITIZER_MEMORY}) 146 | 147 | set_target_properties(chains_options PROPERTIES UNITY_BUILD ${chains_ENABLE_UNITY_BUILD}) 148 | 149 | if(chains_ENABLE_PCH) 150 | target_precompile_headers( 151 | chains_options 152 | INTERFACE 153 | 154 | 155 | ) 156 | endif() 157 | 158 | if(chains_ENABLE_CACHE) 159 | include(cmake/Cache.cmake) 160 | chains_enable_cache() 161 | endif() 162 | 163 | include(cmake/StaticAnalyzers.cmake) 164 | if(chains_ENABLE_CLANG_TIDY) 165 | chains_enable_clang_tidy(chains_options ${chains_WARNINGS_AS_ERRORS}) 166 | endif() 167 | 168 | if(chains_ENABLE_CPPCHECK) 169 | chains_enable_cppcheck(${chains_WARNINGS_AS_ERRORS} "" # override cppcheck options 170 | ) 171 | endif() 172 | 173 | if(chains_ENABLE_COVERAGE) 174 | include(cmake/Tests.cmake) 175 | chains_enable_coverage(chains_options) 176 | endif() 177 | 178 | if(chains_WARNINGS_AS_ERRORS) 179 | check_cxx_compiler_flag("-Wl,--fatal-warnings" LINKER_FATAL_WARNINGS) 180 | if(LINKER_FATAL_WARNINGS) 181 | # This is not working consistently, so disabling for now 182 | # target_link_options(chains_options INTERFACE -Wl,--fatal-warnings) 183 | endif() 184 | endif() 185 | 186 | if(chains_ENABLE_HARDENING AND NOT chains_ENABLE_GLOBAL_HARDENING) 187 | include(cmake/Hardening.cmake) 188 | if(NOT SUPPORTS_UBSAN 189 | OR chains_ENABLE_SANITIZER_UNDEFINED 190 | OR chains_ENABLE_SANITIZER_ADDRESS 191 | OR chains_ENABLE_SANITIZER_THREAD 192 | OR chains_ENABLE_SANITIZER_LEAK) 193 | set(ENABLE_UBSAN_MINIMAL_RUNTIME FALSE) 194 | else() 195 | set(ENABLE_UBSAN_MINIMAL_RUNTIME TRUE) 196 | endif() 197 | chains_enable_hardening(chains_options OFF ${ENABLE_UBSAN_MINIMAL_RUNTIME}) 198 | endif() 199 | 200 | endmacro() 201 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chains 2 | 3 | [![ci](https://github.com/stlab/chains/actions/workflows/ci.yml/badge.svg)](https://github.com/stlab/chains/actions/workflows/ci.yml) 4 | [![codecov](https://codecov.io/gh/stlab/chains/branch/main/graph/badge.svg)](https://codecov.io/gh/stlab/chains) 5 | [![CodeQL](https://github.com/stlab/chains/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/stlab/chains/actions/workflows/codeql-analysis.yml) 6 | 7 | ## About chains 8 | Experimental alternative to sender/receivers. 9 | 10 | 11 | ## More Details 12 | 13 | * [Dependency Setup](README_dependencies.md) 14 | * [Building Details](README_building.md) 15 | * [Troubleshooting](README_troubleshooting.md) 16 | * [Docker](README_docker.md) 17 | -------------------------------------------------------------------------------- /README_building.md: -------------------------------------------------------------------------------- 1 | ## Build Instructions 2 | 3 | A full build has different steps: 4 | 1) Specifying the compiler using environment variables 5 | 2) Configuring the project 6 | 3) Building the project 7 | 8 | For the subsequent builds, in case you change the source code, you only need to repeat the last step. 9 | 10 | ### (1) Specify the compiler using environment variables 11 | 12 | By default (if you don't set environment variables `CC` and `CXX`), the system default compiler will be used. 13 | 14 | CMake uses the environment variables CC and CXX to decide which compiler to use. So to avoid the conflict issues only specify the compilers using these variables. 15 | 16 | 17 |
18 | Commands for setting the compilers 19 | 20 | - Debian/Ubuntu/MacOS: 21 | 22 | Set your desired compiler (`clang`, `gcc`, etc): 23 | 24 | - Temporarily (only for the current shell) 25 | 26 | Run one of the followings in the terminal: 27 | 28 | - clang 29 | 30 | CC=clang CXX=clang++ 31 | 32 | - gcc 33 | 34 | CC=gcc CXX=g++ 35 | 36 | - Permanent: 37 | 38 | Open `~/.bashrc` using your text editor: 39 | 40 | gedit ~/.bashrc 41 | 42 | Add `CC` and `CXX` to point to the compilers: 43 | 44 | export CC=clang 45 | export CXX=clang++ 46 | 47 | Save and close the file. 48 | 49 | - Windows: 50 | 51 | - Permanent: 52 | 53 | Run one of the followings in PowerShell: 54 | 55 | - Visual Studio generator and compiler (cl) 56 | 57 | [Environment]::SetEnvironmentVariable("CC", "cl.exe", "User") 58 | [Environment]::SetEnvironmentVariable("CXX", "cl.exe", "User") 59 | refreshenv 60 | 61 | Set the architecture using [vcvarsall](https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=vs-2019#vcvarsall-syntax): 62 | 63 | vcvarsall.bat x64 64 | 65 | - clang 66 | 67 | [Environment]::SetEnvironmentVariable("CC", "clang.exe", "User") 68 | [Environment]::SetEnvironmentVariable("CXX", "clang++.exe", "User") 69 | refreshenv 70 | 71 | - gcc 72 | 73 | [Environment]::SetEnvironmentVariable("CC", "gcc.exe", "User") 74 | [Environment]::SetEnvironmentVariable("CXX", "g++.exe", "User") 75 | refreshenv 76 | 77 | 78 | - Temporarily (only for the current shell): 79 | 80 | $Env:CC="clang.exe" 81 | $Env:CXX="clang++.exe" 82 | 83 |
84 | 85 | ### (2) Configure your build 86 | 87 | To configure the project, you could use `cmake`, or `ccmake` or `cmake-gui`. Each of them are explained in the following: 88 | 89 | #### (2.a) Configuring via cmake: 90 | With Cmake directly: 91 | 92 | cmake -S . -B ./build 93 | 94 | Cmake will automatically create the `./build` folder if it does not exist, and it wil configure the project. 95 | 96 | Instead, if you have CMake version 3.21+, you can use one of the configuration presets that are listed in the CmakePresets.json file. 97 | 98 | cmake . --preset 99 | cmake --build 100 | 101 | #### (2.b) Configuring via ccmake: 102 | 103 | With the Cmake Curses Dialog Command Line tool: 104 | 105 | ccmake -S . -B ./build 106 | 107 | Once `ccmake` has finished setting up, press 'c' to configure the project, 108 | press 'g' to generate, and 'q' to quit. 109 | 110 | #### (2.c) Configuring via cmake-gui: 111 | 112 | To use the GUI of the cmake: 113 | 114 | 2.c.1) Open cmake-gui from the project directory: 115 | ``` 116 | cmake-gui . 117 | ``` 118 | 2.c.2) Set the build directory: 119 | 120 | ![build_dir](https://user-images.githubusercontent.com/16418197/82524586-fa48e380-9af4-11ea-8514-4e18a063d8eb.jpg) 121 | 122 | 2.c.3) Configure the generator: 123 | 124 | In cmake-gui, from the upper menu select `Tools/Configure`. 125 | 126 | **Warning**: if you have set `CC` and `CXX` always choose the `use default native compilers` option. This picks `CC` and `CXX`. Don't change the compiler at this stage! 127 | 128 |
129 | Windows - MinGW Makefiles 130 | 131 | Choose MinGW Makefiles as the generator: 132 | 133 | mingw 134 | 135 |
136 | 137 |
138 | Windows - Visual Studio generator and compiler 139 | 140 | You should have already set `C` and `CXX` to `cl.exe`. 141 | 142 | Choose "Visual Studio 16 2019" as the generator: 143 | 144 | default_vs 145 | 146 |
147 | 148 |
149 | 150 | Windows - Visual Studio generator and Clang Compiler 151 | 152 | You should have already set `C` and `CXX` to `clang.exe` and `clang++.exe`. 153 | 154 | Choose "Visual Studio 16 2019" as the generator. To tell Visual studio to use `clang-cl.exe`: 155 | - If you use the LLVM that is shipped with Visual Studio: write `ClangCl` under "optional toolset to use". 156 | 157 | visual_studio 158 | 159 | - If you use an external LLVM: write [`LLVM_v142`](https://github.com/zufuliu/llvm-utils#llvm-for-visual-studio-2017-and-2019) 160 | under "optional toolset to use". 161 | 162 | visual_studio 163 | 164 |
165 |
166 | 167 | 2.c.4) Choose the Cmake options and then generate: 168 | 169 | ![generate](https://user-images.githubusercontent.com/16418197/82781591-c97feb80-9e1f-11ea-86c8-f2748b96f516.png) 170 | 171 | ### (3) Build the project 172 | Once you have selected all the options you would like to use, you can build the 173 | project (all targets): 174 | 175 | cmake --build ./build 176 | 177 | For Visual Studio, give the build configuration (Release, RelWithDeb, Debug, etc) like the following: 178 | 179 | cmake --build ./build -- /p:configuration=Release 180 | 181 | 182 | ### Running the tests 183 | 184 | You can use the `ctest` command run the tests. 185 | 186 | ```shell 187 | cd ./build 188 | ctest -C Debug 189 | cd ../ 190 | ``` 191 | 192 | 193 | -------------------------------------------------------------------------------- /README_dependencies.md: -------------------------------------------------------------------------------- 1 | ## Dependencies 2 | 3 | Note about install commands: 4 | - for Windows, we use [choco](https://chocolatey.org/install). 5 | - for MacOS, we use [brew](https://brew.sh/). 6 | - In case of an error in cmake, make sure that the dependencies are on the PATH. 7 | 8 | 9 | ### Too Long, Didn't Install 10 | 11 | This is a really long list of dependencies, and it's easy to mess up. That's why: 12 | 13 | #### Docker 14 | We have a Docker image that's already set up for you. See the [Docker instructions](./README_docker.md). 15 | 16 | #### Setup-cpp 17 | 18 | We have [setup-cpp](https://github.com/aminya/setup-cpp) that is a cross-platform tool to install all the compilers and dependencies on the system. 19 | 20 | Please check [the setup-cpp documentation](https://github.com/aminya/setup-cpp) for more information. 21 | 22 | For example, on Windows, you can run the following to install llvm, cmake, ninja, ccache, and cppcheck. 23 | ```ps1 24 | # windows example (open shell as admin) 25 | curl -LJO "https://github.com/aminya/setup-cpp/releases/download/v0.5.7/setup_cpp_windows.exe" 26 | ./setup_cpp_windows --compiler llvm --cmake true --ninja true --ccache true --cppcheck true 27 | 28 | RefreshEnv.cmd # reload the environment 29 | ``` 30 | 31 | ### Necessary Dependencies 32 | 1. A C++ compiler that supports C++17. 33 | See [cppreference.com](https://en.cppreference.com/w/cpp/compiler_support) 34 | to see which features are supported by each compiler. 35 | The following compilers should work: 36 | 37 | * [gcc 7+](https://gcc.gnu.org/) 38 |
39 | Install command 40 | 41 | - Debian/Ubuntu: 42 | 43 | sudo apt install build-essential 44 | 45 | - Windows: 46 | 47 | choco install mingw -y 48 | 49 | - MacOS: 50 | 51 | brew install gcc 52 |
53 | 54 | * [clang 6+](https://clang.llvm.org/) 55 |
56 | Install command 57 | 58 | - Debian/Ubuntu: 59 | 60 | bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" 61 | 62 | - Windows: 63 | 64 | Visual Studio 2019 ships with LLVM (see the Visual Studio section). However, to install LLVM separately: 65 | 66 | choco install llvm -y 67 | 68 | llvm-utils for using external LLVM with Visual Studio generator: 69 | 70 | git clone https://github.com/zufuliu/llvm-utils.git 71 | cd llvm-utils/VS2017 72 | .\install.bat 73 | 74 | - MacOS: 75 | 76 | brew install llvm 77 |
78 | 79 | * [Visual Studio 2019 or higher](https://visualstudio.microsoft.com/) 80 |
81 | Install command + Environment setup 82 | 83 | On Windows, you need to install Visual Studio 2019 because of the SDK and libraries that ship with it. 84 | 85 | Visual Studio IDE - 2019 Community (installs Clang too): 86 | 87 | choco install -y visualstudio2019community --package-parameters "add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --includeOptional --passive --locale en-US" 88 | 89 | Put MSVC compiler, Clang compiler, and vcvarsall.bat on the path: 90 | 91 | choco install vswhere -y 92 | refreshenv 93 | 94 | # change to x86 for 32bit 95 | $clpath = vswhere -products * -latest -prerelease -find **/Hostx64/x64/* 96 | $clangpath = vswhere -products * -latest -prerelease -find **/Llvm/bin/* 97 | $vcvarsallpath = vswhere -products * -latest -prerelease -find **/Auxiliary/Build/* 98 | 99 | $path = [System.Environment]::GetEnvironmentVariable("PATH", "User") 100 | [Environment]::SetEnvironmentVariable("Path", $path + ";$clpath" + ";$clangpath" + ";$vcvarsallpath", "User") 101 | refreshenv 102 | 103 |
104 | 105 | 106 | 2. [CMake 3.21+](https://cmake.org/) 107 |
108 | Install Command 109 | 110 | - Debian/Ubuntu: 111 | 112 | sudo apt-get install cmake 113 | 114 | - Windows: 115 | 116 | choco install cmake -y 117 | 118 | - MacOS: 119 | 120 | brew install cmake 121 | 122 |
123 | 124 | ### Optional Dependencies 125 | #### C++ Tools 126 | * [Doxygen](http://doxygen.nl/) 127 |
128 | Install Command 129 | 130 | - Debian/Ubuntu: 131 | 132 | sudo apt-get install doxygen 133 | sudo apt-get install graphviz 134 | 135 | - Windows: 136 | 137 | choco install doxygen.install -y 138 | choco install graphviz -y 139 | 140 | - MacOS: 141 | 142 | brew install doxygen 143 | brew install graphviz 144 | 145 |
146 | 147 | 148 | * [ccache](https://ccache.dev/) 149 |
150 | Install Command 151 | 152 | - Debian/Ubuntu: 153 | 154 | sudo apt-get install ccache 155 | 156 | - Windows: 157 | 158 | choco install ccache -y 159 | 160 | - MacOS: 161 | 162 | brew install ccache 163 | 164 |
165 | 166 | 167 | * [Cppcheck](http://cppcheck.sourceforge.net/) 168 |
169 | Install Command 170 | 171 | - Debian/Ubuntu: 172 | 173 | sudo apt-get install cppcheck 174 | 175 | - Windows: 176 | 177 | choco install cppcheck -y 178 | 179 | - MacOS: 180 | 181 | brew install cppcheck 182 | 183 |
184 | 185 | 186 | * [include-what-you-use](https://include-what-you-use.org/) 187 |
188 | Install Command 189 | 190 | Follow instructions here: 191 | https://github.com/include-what-you-use/include-what-you-use#how-to-install 192 |
193 | -------------------------------------------------------------------------------- /README_docker.md: -------------------------------------------------------------------------------- 1 | ## Docker Instructions 2 | 3 | If you have [Docker](https://www.docker.com/) installed, you can run this 4 | in your terminal, when the Dockerfile is inside the `.devcontainer` directory: 5 | 6 | ```bash 7 | docker build -f ./.devcontainer/Dockerfile --tag=my_project:latest . 8 | docker run -it my_project:latest 9 | ``` 10 | 11 | This command will put you in a `bash` session in a Ubuntu 20.04 Docker container, 12 | with all of the tools listed in the [Dependencies](#dependencies) section already installed. 13 | Additionally, you will have `g++-11` and `clang++-13` installed as the default 14 | versions of `g++` and `clang++`. 15 | 16 | If you want to build this container using some other versions of gcc and clang, 17 | you may do so with the `GCC_VER` and `LLVM_VER` arguments: 18 | 19 | ```bash 20 | docker build --tag=myproject:latest --build-arg GCC_VER=10 --build-arg LLVM_VER=11 . 21 | ``` 22 | 23 | The CC and CXX environment variables are set to GCC version 11 by default. 24 | If you wish to use clang as your default CC and CXX environment variables, you 25 | may do so like this: 26 | 27 | ```bash 28 | docker build --tag=my_project:latest --build-arg USE_CLANG=1 . 29 | ``` 30 | 31 | You will be logged in as root, so you will see the `#` symbol as your prompt. 32 | You will be in a directory that contains a copy of the `cpp_starter_project`; 33 | any changes you make to your local copy will not be updated in the Docker image 34 | until you rebuild it. 35 | If you need to mount your local copy directly in the Docker image, see 36 | [Docker volumes docs](https://docs.docker.com/storage/volumes/). 37 | TLDR: 38 | 39 | ```bash 40 | docker run -it \ 41 | -v absolute_path_on_host_machine:absolute_path_in_guest_container \ 42 | my_project:latest 43 | ``` 44 | 45 | You can configure and build [as directed above](#build) using these commands: 46 | 47 | ```bash 48 | /starter_project# mkdir build 49 | /starter_project# cmake -S . -B ./build 50 | /starter_project# cmake --build ./build 51 | ``` 52 | 53 | You can configure and build using `clang-13`, without rebuilding the container, 54 | with these commands: 55 | 56 | ```bash 57 | /starter_project# mkdir build 58 | /starter_project# CC=clang CXX=clang++ cmake -S . -B ./build 59 | /starter_project# cmake --build ./build 60 | ``` 61 | 62 | The `ccmake` tool is also installed; you can substitute `ccmake` for `cmake` to 63 | configure the project interactively. 64 | All of the tools this project supports are installed in the Docker image; 65 | enabling them is as simple as flipping a switch using the `ccmake` interface. 66 | Be aware that some of the sanitizers conflict with each other, so be sure to 67 | run them separately. 68 | 69 | A script called `build_examples.sh` is provided to help you to build the example 70 | GUI projects in this container. 71 | 72 | -------------------------------------------------------------------------------- /cmake/CPM.cmake: -------------------------------------------------------------------------------- 1 | set(CPM_DOWNLOAD_VERSION 0.38.1) 2 | 3 | if(CPM_SOURCE_CACHE) 4 | set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 5 | elseif(DEFINED ENV{CPM_SOURCE_CACHE}) 6 | set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 7 | else() 8 | set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 9 | endif() 10 | 11 | # Expand relative path. This is important if the provided path contains a tilde (~) 12 | get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) 13 | 14 | function(download_cpm) 15 | message(STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}") 16 | file(DOWNLOAD 17 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake 18 | ${CPM_DOWNLOAD_LOCATION} 19 | ) 20 | endfunction() 21 | 22 | if(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION})) 23 | download_cpm() 24 | else() 25 | # resume download if it previously failed 26 | file(READ ${CPM_DOWNLOAD_LOCATION} check) 27 | if("${check}" STREQUAL "") 28 | download_cpm() 29 | endif() 30 | unset(check) 31 | endif() 32 | 33 | include(${CPM_DOWNLOAD_LOCATION}) 34 | -------------------------------------------------------------------------------- /cmake/Cache.cmake: -------------------------------------------------------------------------------- 1 | # Enable cache if available 2 | function(chains_enable_cache) 3 | set(CACHE_OPTION 4 | "ccache" 5 | CACHE STRING "Compiler cache to be used") 6 | set(CACHE_OPTION_VALUES "ccache" "sccache") 7 | set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) 8 | list( 9 | FIND 10 | CACHE_OPTION_VALUES 11 | ${CACHE_OPTION} 12 | CACHE_OPTION_INDEX) 13 | 14 | if(${CACHE_OPTION_INDEX} EQUAL -1) 15 | message( 16 | STATUS 17 | "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}" 18 | ) 19 | endif() 20 | 21 | find_program(CACHE_BINARY NAMES ${CACHE_OPTION_VALUES}) 22 | if(CACHE_BINARY) 23 | message(STATUS "${CACHE_BINARY} found and enabled") 24 | set(CMAKE_CXX_COMPILER_LAUNCHER 25 | ${CACHE_BINARY} 26 | CACHE FILEPATH "CXX compiler cache used") 27 | set(CMAKE_C_COMPILER_LAUNCHER 28 | ${CACHE_BINARY} 29 | CACHE FILEPATH "C compiler cache used") 30 | else() 31 | message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") 32 | endif() 33 | endfunction() 34 | -------------------------------------------------------------------------------- /cmake/CompilerWarnings.cmake: -------------------------------------------------------------------------------- 1 | # from here: 2 | # 3 | # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md 4 | 5 | function( 6 | chains_set_project_warnings 7 | project_name 8 | WARNINGS_AS_ERRORS 9 | MSVC_WARNINGS 10 | CLANG_WARNINGS 11 | GCC_WARNINGS 12 | CUDA_WARNINGS) 13 | if("${MSVC_WARNINGS}" STREQUAL "") 14 | set(MSVC_WARNINGS 15 | /W4 # Baseline reasonable warnings 16 | /w14242 # 'identifier': conversion from 'type1' to 'type2', possible loss of data 17 | /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 18 | /w14263 # 'function': member function does not override any base class virtual member function 19 | /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not 20 | # be destructed correctly 21 | /w14287 # 'operator': unsigned/negative constant mismatch 22 | /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside 23 | # the for-loop scope 24 | /w14296 # 'operator': expression is always 'boolean_value' 25 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 26 | /w14545 # expression before comma evaluates to a function which is missing an argument list 27 | /w14546 # function call before comma missing argument list 28 | /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect 29 | /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? 30 | /w14555 # expression has no effect; expected expression with side- effect 31 | /w14619 # pragma warning: there is no warning number 'number' 32 | /w14640 # Enable warning on thread un-safe static member initialization 33 | /w14826 # Conversion from 'type1' to 'type2' is sign-extended. This may cause unexpected runtime behavior. 34 | /w14905 # wide string literal cast to 'LPSTR' 35 | /w14906 # string literal cast to 'LPWSTR' 36 | /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 37 | /permissive- # standards conformance mode for MSVC compiler. 38 | ) 39 | endif() 40 | 41 | if("${CLANG_WARNINGS}" STREQUAL "") 42 | set(CLANG_WARNINGS 43 | -Wall 44 | -Wextra # reasonable and standard 45 | -Wshadow # warn the user if a variable declaration shadows one from a parent context 46 | -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps 47 | # catch hard to track down memory errors 48 | -Wold-style-cast # warn for c-style casts 49 | -Wcast-align # warn for potential performance problem casts 50 | -Wunused # warn on anything being unused 51 | -Woverloaded-virtual # warn if you overload (not override) a virtual function 52 | -Wpedantic # warn if non-standard C++ is used 53 | -Wconversion # warn on type conversions that may lose data 54 | -Wsign-conversion # warn on sign conversions 55 | -Wnull-dereference # warn if a null dereference is detected 56 | -Wdouble-promotion # warn if float is implicit promoted to double 57 | -Wformat=2 # warn on security issues around functions that format output (ie printf) 58 | -Wimplicit-fallthrough # warn on statements that fallthrough without an explicit annotation 59 | ) 60 | endif() 61 | 62 | if("${GCC_WARNINGS}" STREQUAL "") 63 | set(GCC_WARNINGS 64 | ${CLANG_WARNINGS} 65 | -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist 66 | -Wduplicated-cond # warn if if / else chain has duplicated conditions 67 | -Wduplicated-branches # warn if if / else branches have duplicated code 68 | -Wlogical-op # warn about logical operations being used where bitwise were probably wanted 69 | -Wuseless-cast # warn if you perform a cast to the same type 70 | ) 71 | endif() 72 | 73 | if("${CUDA_WARNINGS}" STREQUAL "") 74 | set(CUDA_WARNINGS 75 | -Wall 76 | -Wextra 77 | -Wunused 78 | -Wconversion 79 | -Wshadow 80 | # TODO add more Cuda warnings 81 | ) 82 | endif() 83 | 84 | if(WARNINGS_AS_ERRORS) 85 | message(TRACE "Warnings are treated as errors") 86 | list(APPEND CLANG_WARNINGS -Werror) 87 | list(APPEND GCC_WARNINGS -Werror) 88 | list(APPEND MSVC_WARNINGS /WX) 89 | endif() 90 | 91 | if(MSVC) 92 | set(PROJECT_WARNINGS_CXX ${MSVC_WARNINGS}) 93 | elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 94 | set(PROJECT_WARNINGS_CXX ${CLANG_WARNINGS}) 95 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 96 | set(PROJECT_WARNINGS_CXX ${GCC_WARNINGS}) 97 | else() 98 | message(AUTHOR_WARNING "No compiler warnings set for CXX compiler: '${CMAKE_CXX_COMPILER_ID}'") 99 | # TODO support Intel compiler 100 | endif() 101 | 102 | # use the same warning flags for C 103 | set(PROJECT_WARNINGS_C "${PROJECT_WARNINGS_CXX}") 104 | 105 | set(PROJECT_WARNINGS_CUDA "${CUDA_WARNINGS}") 106 | 107 | target_compile_options( 108 | ${project_name} 109 | INTERFACE # C++ warnings 110 | $<$:${PROJECT_WARNINGS_CXX}> 111 | # C warnings 112 | $<$:${PROJECT_WARNINGS_C}> 113 | # Cuda warnings 114 | $<$:${PROJECT_WARNINGS_CUDA}>) 115 | endfunction() 116 | -------------------------------------------------------------------------------- /cmake/Cuda.cmake: -------------------------------------------------------------------------------- 1 | # ! target_link_cuda 2 | # A function that links Cuda to the given target 3 | # 4 | # # Example 5 | # add_executable(main_cuda main.cu) 6 | # target_compile_features(main_cuda PRIVATE cxx_std_17) 7 | # target_link_libraries(main_cuda PRIVATE project_options project_warnings) 8 | # target_link_cuda(main_cuda) 9 | # 10 | macro(chains_target_link_cuda target) 11 | # optional named CUDA_WARNINGS 12 | set(oneValueArgs CUDA_WARNINGS) 13 | cmake_parse_arguments( 14 | _cuda_args 15 | "" 16 | "${oneValueArgs}" 17 | "" 18 | ${ARGN}) 19 | 20 | # add CUDA to cmake language 21 | enable_language(CUDA) 22 | 23 | # use the same C++ standard if not specified 24 | if("${CMAKE_CUDA_STANDARD}" STREQUAL "") 25 | set(CMAKE_CUDA_STANDARD "${CMAKE_CXX_STANDARD}") 26 | endif() 27 | 28 | # -fPIC 29 | set_target_properties(${target} PROPERTIES POSITION_INDEPENDENT_CODE ON) 30 | 31 | # We need to explicitly state that we need all CUDA files in the 32 | # ${target} library to be built with -dc as the member functions 33 | # could be called by other libraries and executables 34 | set_target_properties(${target} PROPERTIES CUDA_SEPARABLE_COMPILATION ON) 35 | 36 | if(APPLE) 37 | # We need to add the path to the driver (libcuda.dylib) as an rpath, 38 | # so that the static cuda runtime can find it at runtime. 39 | set_property(TARGET ${target} PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) 40 | endif() 41 | 42 | if(WIN32 AND "$ENV{VSCMD_VER}" STREQUAL "") 43 | message( 44 | WARNING 45 | "Compiling Cuda on Windows outside the Visual Studio Commant prompt or without running `vcvarsall.bat x64` probably fails" 46 | ) 47 | endif() 48 | endmacro() 49 | -------------------------------------------------------------------------------- /cmake/Doxygen.cmake: -------------------------------------------------------------------------------- 1 | # Enable doxygen doc builds of source 2 | function(chains_enable_doxygen DOXYGEN_THEME) 3 | # If not specified, use the top readme file as the first page 4 | if((NOT DOXYGEN_USE_MDFILE_AS_MAINPAGE) AND EXISTS "${PROJECT_SOURCE_DIR}/README.md") 5 | set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${PROJECT_SOURCE_DIR}/README.md") 6 | endif() 7 | 8 | # set better defaults for doxygen 9 | is_verbose(_is_verbose) 10 | if(NOT ${_is_verbose}) 11 | set(DOXYGEN_QUIET YES) 12 | endif() 13 | set(DOXYGEN_CALLER_GRAPH YES) 14 | set(DOXYGEN_CALL_GRAPH YES) 15 | set(DOXYGEN_EXTRACT_ALL YES) 16 | set(DOXYGEN_GENERATE_TREEVIEW YES) 17 | # svg files are much smaller than jpeg and png, and yet they have higher quality 18 | set(DOXYGEN_DOT_IMAGE_FORMAT svg) 19 | set(DOXYGEN_DOT_TRANSPARENT YES) 20 | 21 | # If not specified, exclude the vcpkg files and the files CMake downloads under _deps (like project_options) 22 | if(NOT DOXYGEN_EXCLUDE_PATTERNS) 23 | set(DOXYGEN_EXCLUDE_PATTERNS "${CMAKE_CURRENT_BINARY_DIR}/vcpkg_installed/*" "${CMAKE_CURRENT_BINARY_DIR}/_deps/*") 24 | endif() 25 | 26 | if("${DOXYGEN_THEME}" STREQUAL "") 27 | set(DOXYGEN_THEME "awesome-sidebar") 28 | endif() 29 | 30 | if("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") 31 | # use a modern doxygen theme 32 | # https://github.com/jothepro/doxygen-awesome-css v1.6.1 33 | FetchContent_Declare(_doxygen_theme 34 | URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v1.6.1.zip) 35 | FetchContent_MakeAvailable(_doxygen_theme) 36 | if("${DOXYGEN_THEME}" STREQUAL "awesome" OR "${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") 37 | set(DOXYGEN_HTML_EXTRA_STYLESHEET "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome.css") 38 | endif() 39 | if("${DOXYGEN_THEME}" STREQUAL "awesome-sidebar") 40 | set(DOXYGEN_HTML_EXTRA_STYLESHEET ${DOXYGEN_HTML_EXTRA_STYLESHEET} 41 | "${_doxygen_theme_SOURCE_DIR}/doxygen-awesome-sidebar-only.css") 42 | endif() 43 | else() 44 | # use the original doxygen theme 45 | endif() 46 | 47 | # find doxygen and dot if available 48 | find_package(Doxygen REQUIRED OPTIONAL_COMPONENTS dot) 49 | 50 | # add doxygen-docs target 51 | message(STATUS "Adding `doxygen-docs` target that builds the documentation.") 52 | doxygen_add_docs(doxygen-docs ALL ${PROJECT_SOURCE_DIR} 53 | COMMENT "Generating documentation - entry file: ${CMAKE_CURRENT_BINARY_DIR}/html/index.html") 54 | endfunction() 55 | -------------------------------------------------------------------------------- /cmake/Hardening.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCXXCompilerFlag) 2 | 3 | macro( 4 | chains_enable_hardening 5 | target 6 | global 7 | ubsan_minimal_runtime) 8 | 9 | message(STATUS "** Enabling Hardening (Target ${target}) **") 10 | 11 | if(MSVC) 12 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} /sdl /DYNAMICBASE /guard:cf") 13 | message(STATUS "*** MSVC flags: /sdl /DYNAMICBASE /guard:cf /NXCOMPAT /CETCOMPAT") 14 | set(NEW_LINK_OPTIONS "${NEW_LINK_OPTIONS} /NXCOMPAT /CETCOMPAT") 15 | 16 | elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang|GNU") 17 | set(NEW_CXX_DEFINITIONS "${NEW_CXX_DEFINITIONS} -D_GLIBCXX_ASSERTIONS") 18 | message(STATUS "*** GLIBC++ Assertions (vector[], string[], ...) enabled") 19 | 20 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3") 21 | message(STATUS "*** g++/clang _FORTIFY_SOURCE=3 enabled") 22 | 23 | # check_cxx_compiler_flag(-fpie PIE) 24 | #if(PIE) 25 | # set(NEW_COMPILE_OPTIONS ${NEW_COMPILE_OPTIONS} -fpie) 26 | # set(NEW_LINK_OPTIONS ${NEW_LINK_OPTIONS} -pie) 27 | # 28 | # message(STATUS "*** g++/clang PIE mode enabled") 29 | #else() 30 | # message(STATUS "*** g++/clang PIE mode NOT enabled (not supported)") 31 | #endif() 32 | 33 | check_cxx_compiler_flag(-fstack-protector-strong STACK_PROTECTOR) 34 | if(STACK_PROTECTOR) 35 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fstack-protector-strong") 36 | message(STATUS "*** g++/clang -fstack-protector-strong enabled") 37 | else() 38 | message(STATUS "*** g++/clang -fstack-protector-strong NOT enabled (not supported)") 39 | endif() 40 | 41 | check_cxx_compiler_flag(-fcf-protection CF_PROTECTION) 42 | if(CF_PROTECTION) 43 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fcf-protection") 44 | message(STATUS "*** g++/clang -fcf-protection enabled") 45 | else() 46 | message(STATUS "*** g++/clang -fcf-protection NOT enabled (not supported)") 47 | endif() 48 | 49 | check_cxx_compiler_flag(-fstack-clash-protection CLASH_PROTECTION) 50 | if(CLASH_PROTECTION) 51 | if(LINUX OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") 52 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fstack-clash-protection") 53 | message(STATUS "*** g++/clang -fstack-clash-protection enabled") 54 | else() 55 | message(STATUS "*** g++/clang -fstack-clash-protection NOT enabled (clang on non-Linux)") 56 | endif() 57 | else() 58 | message(STATUS "*** g++/clang -fstack-clash-protection NOT enabled (not supported)") 59 | endif() 60 | endif() 61 | 62 | if(${ubsan_minimal_runtime}) 63 | check_cxx_compiler_flag("-fsanitize=undefined -fno-sanitize-recover=undefined -fsanitize-minimal-runtime" 64 | MINIMAL_RUNTIME) 65 | if(MINIMAL_RUNTIME) 66 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fsanitize=undefined -fsanitize-minimal-runtime") 67 | set(NEW_LINK_OPTIONS "${NEW_LINK_OPTIONS} -fsanitize=undefined -fsanitize-minimal-runtime") 68 | 69 | if(NOT ${global}) 70 | set(NEW_COMPILE_OPTIONS "${NEW_COMPILE_OPTIONS} -fno-sanitize-recover=undefined") 71 | set(NEW_LINK_OPTIONS "${NEW_LINK_OPTIONS} -fno-sanitize-recover=undefined") 72 | else() 73 | message(STATUS "** not enabling -fno-sanitize-recover=undefined for global consumption") 74 | endif() 75 | 76 | message(STATUS "*** ubsan minimal runtime enabled") 77 | else() 78 | message(STATUS "*** ubsan minimal runtime NOT enabled (not supported)") 79 | endif() 80 | else() 81 | message(STATUS "*** ubsan minimal runtime NOT enabled (not requested)") 82 | endif() 83 | 84 | message(STATUS "** Hardening Compiler Flags: ${NEW_COMPILE_OPTIONS}") 85 | message(STATUS "** Hardening Linker Flags: ${NEW_LINK_OPTIONS}") 86 | message(STATUS "** Hardening Compiler Defines: ${NEW_CXX_DEFINITIONS}") 87 | 88 | if(${global}) 89 | message(STATUS "** Setting hardening options globally for all dependencies") 90 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NEW_COMPILE_OPTIONS}") 91 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${NEW_LINK_OPTIONS}") 92 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NEW_CXX_DEFINITIONS}") 93 | else() 94 | target_compile_options(${target} INTERFACE ${NEW_COMPILE_OPTIONS}) 95 | target_link_options(${target} INTERFACE ${NEW_LINK_OPTIONS}) 96 | target_compile_definitions(${target} INTERFACE ${NEW_CXX_DEFINITIONS}) 97 | endif() 98 | endmacro() 99 | -------------------------------------------------------------------------------- /cmake/InterproceduralOptimization.cmake: -------------------------------------------------------------------------------- 1 | macro(chains_enable_ipo) 2 | include(CheckIPOSupported) 3 | check_ipo_supported(RESULT result OUTPUT output) 4 | if(result) 5 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) 6 | else() 7 | message(SEND_ERROR "IPO is not supported: ${output}") 8 | endif() 9 | endmacro() 10 | -------------------------------------------------------------------------------- /cmake/LibFuzzer.cmake: -------------------------------------------------------------------------------- 1 | function(chains_check_libfuzzer_support var_name) 2 | set(LibFuzzerTestSource 3 | " 4 | #include 5 | 6 | extern \"C\" int LLVMFuzzerTestOneInput(const std::uint8_t *data, std::size_t size) { 7 | return 0; 8 | } 9 | ") 10 | 11 | include(CheckCXXSourceCompiles) 12 | 13 | set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer") 14 | set(CMAKE_REQUIRED_LINK_OPTIONS "-fsanitize=fuzzer") 15 | check_cxx_source_compiles("${LibFuzzerTestSource}" ${var_name}) 16 | 17 | endfunction() 18 | -------------------------------------------------------------------------------- /cmake/Linker.cmake: -------------------------------------------------------------------------------- 1 | macro(chains_configure_linker project_name) 2 | include(CheckCXXCompilerFlag) 3 | 4 | set(USER_LINKER_OPTION 5 | "lld" 6 | CACHE STRING "Linker to be used") 7 | set(USER_LINKER_OPTION_VALUES "lld" "gold" "bfd" "mold") 8 | set_property(CACHE USER_LINKER_OPTION PROPERTY STRINGS ${USER_LINKER_OPTION_VALUES}) 9 | list( 10 | FIND 11 | USER_LINKER_OPTION_VALUES 12 | ${USER_LINKER_OPTION} 13 | USER_LINKER_OPTION_INDEX) 14 | 15 | if(${USER_LINKER_OPTION_INDEX} EQUAL -1) 16 | message( 17 | STATUS 18 | "Using custom linker: '${USER_LINKER_OPTION}', explicitly supported entries are ${USER_LINKER_OPTION_VALUES}") 19 | endif() 20 | 21 | if(NOT ENABLE_USER_LINKER) 22 | return() 23 | endif() 24 | 25 | set(LINKER_FLAG "-fuse-ld=${USER_LINKER_OPTION}") 26 | 27 | check_cxx_compiler_flag(${LINKER_FLAG} CXX_SUPPORTS_USER_LINKER) 28 | if(CXX_SUPPORTS_USER_LINKER) 29 | target_compile_options(${project_name} INTERFACE ${LINKER_FLAG}) 30 | endif() 31 | endmacro() 32 | -------------------------------------------------------------------------------- /cmake/PackageProject.cmake: -------------------------------------------------------------------------------- 1 | # Uses ycm (permissive BSD-3-Clause license) and ForwardArguments (permissive MIT license) 2 | 3 | function(chains_package_project) 4 | cmake_policy(SET CMP0103 NEW) # disallow multiple calls with the same NAME 5 | 6 | set(_options ARCH_INDEPENDENT # default to false 7 | ) 8 | set(_oneValueArgs 9 | # default to the project_name: 10 | NAME 11 | COMPONENT 12 | # default to project version: 13 | VERSION 14 | # default to semver 15 | COMPATIBILITY 16 | # default to ${CMAKE_BINARY_DIR} 17 | CONFIG_EXPORT_DESTINATION 18 | # default to ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/${NAME} suitable for vcpkg, etc. 19 | CONFIG_INSTALL_DESTINATION) 20 | set(_multiValueArgs 21 | # recursively found for the current folder if not specified 22 | TARGETS 23 | # a list of public/interface include directories or files 24 | PUBLIC_INCLUDES 25 | # the names of the INTERFACE/PUBLIC dependencies that are found using `CONFIG` 26 | PUBLIC_DEPENDENCIES_CONFIGURED 27 | # the INTERFACE/PUBLIC dependencies that are found by any means using `find_dependency`. 28 | # the arguments must be specified within double quotes (e.g. " 1.0.0 EXACT" or " CONFIG"). 29 | PUBLIC_DEPENDENCIES 30 | # the names of the PRIVATE dependencies that are found using `CONFIG`. Only included when BUILD_SHARED_LIBS is OFF. 31 | PRIVATE_DEPENDENCIES_CONFIGURED 32 | # PRIVATE dependencies that are only included when BUILD_SHARED_LIBS is OFF 33 | PRIVATE_DEPENDENCIES) 34 | 35 | cmake_parse_arguments( 36 | _PackageProject 37 | "${_options}" 38 | "${_oneValueArgs}" 39 | "${_multiValueArgs}" 40 | "${ARGN}") 41 | 42 | # Set default options 43 | include(GNUInstallDirs) # Define GNU standard installation directories such as CMAKE_INSTALL_DATADIR 44 | 45 | # set default packaged targets 46 | if(NOT _PackageProject_TARGETS) 47 | get_all_installable_targets(_PackageProject_TARGETS) 48 | message(STATUS "package_project: considering ${_PackageProject_TARGETS} as the exported targets") 49 | endif() 50 | 51 | # default to the name of the project or the given name 52 | if("${_PackageProject_NAME}" STREQUAL "") 53 | set(_PackageProject_NAME ${PROJECT_NAME}) 54 | endif() 55 | # ycm args 56 | set(_PackageProject_NAMESPACE "${_PackageProject_NAME}::") 57 | set(_PackageProject_VARS_PREFIX ${_PackageProject_NAME}) 58 | set(_PackageProject_EXPORT ${_PackageProject_NAME}) 59 | 60 | # default version to the project version 61 | if("${_PackageProject_VERSION}" STREQUAL "") 62 | set(_PackageProject_VERSION ${PROJECT_VERSION}) 63 | endif() 64 | 65 | # default compatibility to SameMajorVersion 66 | if("${_PackageProject_COMPATIBILITY}" STREQUAL "") 67 | set(_PackageProject_COMPATIBILITY "SameMajorVersion") 68 | endif() 69 | 70 | # default to the build directory 71 | if("${_PackageProject_CONFIG_EXPORT_DESTINATION}" STREQUAL "") 72 | set(_PackageProject_CONFIG_EXPORT_DESTINATION "${CMAKE_BINARY_DIR}") 73 | endif() 74 | set(_PackageProject_EXPORT_DESTINATION "${_PackageProject_CONFIG_EXPORT_DESTINATION}") 75 | 76 | # use datadir (works better with vcpkg, etc) 77 | if("${_PackageProject_CONFIG_INSTALL_DESTINATION}" STREQUAL "") 78 | set(_PackageProject_CONFIG_INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/${_PackageProject_NAME}") 79 | endif() 80 | # ycm args 81 | set(_PackageProject_INSTALL_DESTINATION "${_PackageProject_CONFIG_INSTALL_DESTINATION}") 82 | 83 | # Installation of the public/interface includes 84 | if(NOT 85 | "${_PackageProject_PUBLIC_INCLUDES}" 86 | STREQUAL 87 | "") 88 | foreach(_INC ${_PackageProject_PUBLIC_INCLUDES}) 89 | # make include absolute 90 | if(NOT IS_ABSOLUTE ${_INC}) 91 | set(_INC "${CMAKE_CURRENT_SOURCE_DIR}/${_INC}") 92 | endif() 93 | # install include 94 | if(IS_DIRECTORY ${_INC}) 95 | # the include directories are directly installed to the install destination. If you want an `include` folder in the install destination, name your include directory as `include` (or install it manually using `install()` command). 96 | install(DIRECTORY ${_INC} DESTINATION "./") 97 | else() 98 | install(FILES ${_INC} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 99 | endif() 100 | endforeach() 101 | endif() 102 | 103 | # Append the configured public dependencies 104 | if(NOT 105 | "${_PackageProject_PUBLIC_DEPENDENCIES_CONFIGURED}" 106 | STREQUAL 107 | "") 108 | set(_PUBLIC_DEPENDENCIES_CONFIG) 109 | foreach(DEP ${_PackageProject_PUBLIC_DEPENDENCIES_CONFIGURED}) 110 | list(APPEND _PUBLIC_DEPENDENCIES_CONFIG "${DEP} CONFIG") 111 | endforeach() 112 | endif() 113 | list(APPEND _PackageProject_PUBLIC_DEPENDENCIES ${_PUBLIC_DEPENDENCIES_CONFIG}) 114 | # ycm arg 115 | set(_PackageProject_DEPENDENCIES ${_PackageProject_PUBLIC_DEPENDENCIES}) 116 | 117 | # Append the configured private dependencies 118 | if(NOT 119 | "${_PackageProject_PRIVATE_DEPENDENCIES_CONFIGURED}" 120 | STREQUAL 121 | "") 122 | set(_PRIVATE_DEPENDENCIES_CONFIG) 123 | foreach(DEP ${_PackageProject_PRIVATE_DEPENDENCIES_CONFIGURED}) 124 | list(APPEND _PRIVATE_DEPENDENCIES_CONFIG "${DEP} CONFIG") 125 | endforeach() 126 | endif() 127 | # ycm arg 128 | list(APPEND _PackageProject_PRIVATE_DEPENDENCIES ${_PRIVATE_DEPENDENCIES_CONFIG}) 129 | 130 | # Installation of package (compatible with vcpkg, etc) 131 | install( 132 | TARGETS ${_PackageProject_TARGETS} 133 | EXPORT ${_PackageProject_EXPORT} 134 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib 135 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT lib 136 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin 137 | PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_PackageProject_NAME}" COMPONENT dev) 138 | 139 | # install the usage file 140 | set(_targets_str "") 141 | foreach(_target ${_PackageProject_TARGETS}) 142 | set(_targets_str "${_targets_str} ${_PackageProject_NAMESPACE}${_target}") 143 | endforeach() 144 | set(USAGE_FILE_CONTENT 145 | "The package ${_PackageProject_NAME} provides CMake targets: 146 | 147 | find_package(${_PackageProject_NAME} CONFIG REQUIRED) 148 | target_link_libraries(main PRIVATE ${_targets_str}) 149 | ") 150 | install(CODE "MESSAGE(STATUS \"${USAGE_FILE_CONTENT}\")") 151 | file(WRITE "${_PackageProject_EXPORT_DESTINATION}/usage" "${USAGE_FILE_CONTENT}") 152 | install(FILES "${_PackageProject_EXPORT_DESTINATION}/usage" 153 | DESTINATION "${_PackageProject_CONFIG_INSTALL_DESTINATION}") 154 | 155 | unset(_PackageProject_TARGETS) 156 | 157 | # download ForwardArguments 158 | FetchContent_Declare( 159 | _fargs 160 | URL https://github.com/polysquare/cmake-forward-arguments/archive/8c50d1f956172edb34e95efa52a2d5cb1f686ed2.zip) 161 | FetchContent_GetProperties(_fargs) 162 | if(NOT _fargs_POPULATED) 163 | FetchContent_Populate(_fargs) 164 | endif() 165 | include("${_fargs_SOURCE_DIR}/ForwardArguments.cmake") 166 | 167 | # prepare the forward arguments for ycm 168 | set(_FARGS_LIST) 169 | cmake_forward_arguments( 170 | _PackageProject 171 | _FARGS_LIST 172 | OPTION_ARGS 173 | "${_options};" 174 | SINGLEVAR_ARGS 175 | "${_oneValueArgs};EXPORT_DESTINATION;INSTALL_DESTINATION;NAMESPACE;VARS_PREFIX;EXPORT" 176 | MULTIVAR_ARGS 177 | "${_multiValueArgs};DEPENDENCIES;PRIVATE_DEPENDENCIES") 178 | 179 | # download ycm 180 | FetchContent_Declare(_ycm URL https://github.com/robotology/ycm/archive/refs/tags/v0.13.0.zip) 181 | FetchContent_GetProperties(_ycm) 182 | if(NOT _ycm_POPULATED) 183 | FetchContent_Populate(_ycm) 184 | endif() 185 | include("${_ycm_SOURCE_DIR}/modules/InstallBasicPackageFiles.cmake") 186 | 187 | install_basic_package_files(${_PackageProject_NAME} "${_FARGS_LIST}") 188 | 189 | include("${_ycm_SOURCE_DIR}/modules/AddUninstallTarget.cmake") 190 | endfunction() 191 | -------------------------------------------------------------------------------- /cmake/PreventInSourceBuilds.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This function will prevent in-source builds 3 | # 4 | function(chains_assure_out_of_source_builds) 5 | # make sure the user doesn't play dirty with symlinks 6 | get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) 7 | get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH) 8 | 9 | # disallow in-source builds 10 | if("${srcdir}" STREQUAL "${bindir}") 11 | message("######################################################") 12 | message("Warning: in-source builds are disabled") 13 | message("Please create a separate build directory and run cmake from there") 14 | message("######################################################") 15 | message(FATAL_ERROR "Quitting configuration") 16 | endif() 17 | endfunction() 18 | 19 | chains_assure_out_of_source_builds() 20 | -------------------------------------------------------------------------------- /cmake/Sanitizers.cmake: -------------------------------------------------------------------------------- 1 | function( 2 | chains_enable_sanitizers 3 | project_name 4 | ENABLE_SANITIZER_ADDRESS 5 | ENABLE_SANITIZER_LEAK 6 | ENABLE_SANITIZER_UNDEFINED_BEHAVIOR 7 | ENABLE_SANITIZER_THREAD 8 | ENABLE_SANITIZER_MEMORY) 9 | 10 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 11 | set(SANITIZERS "") 12 | 13 | if(${ENABLE_SANITIZER_ADDRESS}) 14 | list(APPEND SANITIZERS "address") 15 | endif() 16 | 17 | if(${ENABLE_SANITIZER_LEAK}) 18 | list(APPEND SANITIZERS "leak") 19 | endif() 20 | 21 | if(${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR}) 22 | list(APPEND SANITIZERS "undefined") 23 | endif() 24 | 25 | if(${ENABLE_SANITIZER_THREAD}) 26 | if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) 27 | message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") 28 | else() 29 | list(APPEND SANITIZERS "thread") 30 | endif() 31 | endif() 32 | 33 | if(${ENABLE_SANITIZER_MEMORY} AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 34 | message( 35 | WARNING 36 | "Memory sanitizer requires all the code (including libc++) to be MSan-instrumented otherwise it reports false positives" 37 | ) 38 | if("address" IN_LIST SANITIZERS 39 | OR "thread" IN_LIST SANITIZERS 40 | OR "leak" IN_LIST SANITIZERS) 41 | message(WARNING "Memory sanitizer does not work with Address, Thread or Leak sanitizer enabled") 42 | else() 43 | list(APPEND SANITIZERS "memory") 44 | endif() 45 | endif() 46 | elseif(MSVC) 47 | if(${ENABLE_SANITIZER_ADDRESS}) 48 | list(APPEND SANITIZERS "address") 49 | endif() 50 | if(${ENABLE_SANITIZER_LEAK} 51 | OR ${ENABLE_SANITIZER_UNDEFINED_BEHAVIOR} 52 | OR ${ENABLE_SANITIZER_THREAD} 53 | OR ${ENABLE_SANITIZER_MEMORY}) 54 | message(WARNING "MSVC only supports address sanitizer") 55 | endif() 56 | endif() 57 | 58 | list( 59 | JOIN 60 | SANITIZERS 61 | "," 62 | LIST_OF_SANITIZERS) 63 | 64 | if(LIST_OF_SANITIZERS) 65 | if(NOT 66 | "${LIST_OF_SANITIZERS}" 67 | STREQUAL 68 | "") 69 | if(NOT MSVC) 70 | target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 71 | target_link_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 72 | else() 73 | string(FIND "$ENV{PATH}" "$ENV{VSINSTALLDIR}" index_of_vs_install_dir) 74 | if("${index_of_vs_install_dir}" STREQUAL "-1") 75 | message( 76 | SEND_ERROR 77 | "Using MSVC sanitizers requires setting the MSVC environment before building the project. Please manually open the MSVC command prompt and rebuild the project." 78 | ) 79 | endif() 80 | target_compile_options(${project_name} INTERFACE /fsanitize=${LIST_OF_SANITIZERS} /Zi /INCREMENTAL:NO) 81 | target_compile_definitions(${project_name} INTERFACE _DISABLE_VECTOR_ANNOTATION _DISABLE_STRING_ANNOTATION) 82 | target_link_options(${project_name} INTERFACE /INCREMENTAL:NO) 83 | endif() 84 | endif() 85 | endif() 86 | 87 | endfunction() 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /cmake/StandardProjectSettings.cmake: -------------------------------------------------------------------------------- 1 | # Set a default build type if none was specified 2 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 3 | message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") 4 | set(CMAKE_BUILD_TYPE 5 | RelWithDebInfo 6 | CACHE STRING "Choose the type of build." FORCE) 7 | # Set the possible values of build type for cmake-gui, ccmake 8 | set_property( 9 | CACHE CMAKE_BUILD_TYPE 10 | PROPERTY STRINGS 11 | "Debug" 12 | "Release" 13 | "MinSizeRel" 14 | "RelWithDebInfo") 15 | endif() 16 | 17 | # Generate compile_commands.json to make it easier to work with clang based tools 18 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | 20 | # Enhance error reporting and compiler messages 21 | if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 22 | if(WIN32) 23 | # On Windows cuda nvcc uses cl and not clang 24 | add_compile_options($<$:-fcolor-diagnostics> $<$:-fcolor-diagnostics>) 25 | else() 26 | add_compile_options(-fcolor-diagnostics) 27 | endif() 28 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 29 | if(WIN32) 30 | # On Windows cuda nvcc uses cl and not gcc 31 | add_compile_options($<$:-fdiagnostics-color=always> 32 | $<$:-fdiagnostics-color=always>) 33 | else() 34 | add_compile_options(-fdiagnostics-color=always) 35 | endif() 36 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER 1900) 37 | add_compile_options(/diagnostics:column) 38 | else() 39 | message(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 40 | endif() 41 | 42 | 43 | # run vcvarsall when msvc is used 44 | include("${CMAKE_CURRENT_LIST_DIR}/VCEnvironment.cmake") 45 | run_vcvarsall() 46 | -------------------------------------------------------------------------------- /cmake/StaticAnalyzers.cmake: -------------------------------------------------------------------------------- 1 | macro(chains_enable_cppcheck WARNINGS_AS_ERRORS CPPCHECK_OPTIONS) 2 | find_program(CPPCHECK cppcheck) 3 | if(CPPCHECK) 4 | 5 | if(CMAKE_GENERATOR MATCHES ".*Visual Studio.*") 6 | set(CPPCHECK_TEMPLATE "vs") 7 | else() 8 | set(CPPCHECK_TEMPLATE "gcc") 9 | endif() 10 | 11 | if("${CPPCHECK_OPTIONS}" STREQUAL "") 12 | # Enable all warnings that are actionable by the user of this toolset 13 | # style should enable the other 3, but we'll be explicit just in case 14 | set(CMAKE_CXX_CPPCHECK 15 | ${CPPCHECK} 16 | --template=${CPPCHECK_TEMPLATE} 17 | --enable=style,performance,warning,portability 18 | --inline-suppr 19 | # We cannot act on a bug/missing feature of cppcheck 20 | --suppress=cppcheckError 21 | --suppress=internalAstError 22 | # if a file does not have an internalAstError, we get an unmatchedSuppression error 23 | --suppress=unmatchedSuppression 24 | # noisy and incorrect sometimes 25 | --suppress=passedByValue 26 | # ignores code that cppcheck thinks is invalid C++ 27 | --suppress=syntaxError 28 | --suppress=preprocessorErrorDirective 29 | --inconclusive) 30 | else() 31 | # if the user provides a CPPCHECK_OPTIONS with a template specified, it will override this template 32 | set(CMAKE_CXX_CPPCHECK ${CPPCHECK} --template=${CPPCHECK_TEMPLATE} ${CPPCHECK_OPTIONS}) 33 | endif() 34 | 35 | if(NOT 36 | "${CMAKE_CXX_STANDARD}" 37 | STREQUAL 38 | "") 39 | set(CMAKE_CXX_CPPCHECK ${CMAKE_CXX_CPPCHECK} --std=c++${CMAKE_CXX_STANDARD}) 40 | endif() 41 | if(${WARNINGS_AS_ERRORS}) 42 | list(APPEND CMAKE_CXX_CPPCHECK --error-exitcode=2) 43 | endif() 44 | else() 45 | message(${WARNING_MESSAGE} "cppcheck requested but executable not found") 46 | endif() 47 | endmacro() 48 | 49 | macro(chains_enable_clang_tidy target WARNINGS_AS_ERRORS) 50 | 51 | find_program(CLANGTIDY clang-tidy) 52 | if(CLANGTIDY) 53 | if(NOT 54 | CMAKE_CXX_COMPILER_ID 55 | MATCHES 56 | ".*Clang") 57 | 58 | get_target_property(TARGET_PCH ${target} INTERFACE_PRECOMPILE_HEADERS) 59 | 60 | if("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND") 61 | get_target_property(TARGET_PCH ${target} PRECOMPILE_HEADERS) 62 | endif() 63 | 64 | if(NOT ("${TARGET_PCH}" STREQUAL "TARGET_PCH-NOTFOUND")) 65 | message( 66 | SEND_ERROR 67 | "clang-tidy cannot be enabled with non-clang compiler and PCH, clang-tidy fails to handle gcc's PCH file") 68 | endif() 69 | endif() 70 | 71 | # construct the clang-tidy command line 72 | set(CLANG_TIDY_OPTIONS 73 | ${CLANGTIDY} 74 | -extra-arg=-Wno-unknown-warning-option 75 | -extra-arg=-Wno-ignored-optimization-argument 76 | -extra-arg=-Wno-unused-command-line-argument 77 | -p) 78 | # set standard 79 | if(NOT 80 | "${CMAKE_CXX_STANDARD}" 81 | STREQUAL 82 | "") 83 | if("${CLANG_TIDY_OPTIONS_DRIVER_MODE}" STREQUAL "cl") 84 | set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=/std:c++${CMAKE_CXX_STANDARD}) 85 | else() 86 | set(CLANG_TIDY_OPTIONS ${CLANG_TIDY_OPTIONS} -extra-arg=-std=c++${CMAKE_CXX_STANDARD}) 87 | endif() 88 | endif() 89 | 90 | # set warnings as errors 91 | if(${WARNINGS_AS_ERRORS}) 92 | list(APPEND CLANG_TIDY_OPTIONS -warnings-as-errors=*) 93 | endif() 94 | 95 | message("Also setting clang-tidy globally") 96 | set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_OPTIONS}) 97 | else() 98 | message(${WARNING_MESSAGE} "clang-tidy requested but executable not found") 99 | endif() 100 | endmacro() 101 | 102 | macro(chains_enable_include_what_you_use) 103 | find_program(INCLUDE_WHAT_YOU_USE include-what-you-use) 104 | if(INCLUDE_WHAT_YOU_USE) 105 | set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) 106 | else() 107 | message(${WARNING_MESSAGE} "include-what-you-use requested but executable not found") 108 | endif() 109 | endmacro() 110 | -------------------------------------------------------------------------------- /cmake/SystemLink.cmake: -------------------------------------------------------------------------------- 1 | # Include a system directory (which suppresses its warnings). 2 | function(target_include_system_directories target) 3 | set(multiValueArgs INTERFACE PUBLIC PRIVATE) 4 | cmake_parse_arguments( 5 | ARG 6 | "" 7 | "" 8 | "${multiValueArgs}" 9 | ${ARGN}) 10 | 11 | foreach(scope IN ITEMS INTERFACE PUBLIC PRIVATE) 12 | foreach(lib_include_dirs IN LISTS ARG_${scope}) 13 | if(NOT MSVC) 14 | # system includes do not work in MSVC 15 | # awaiting https://gitlab.kitware.com/cmake/cmake/-/issues/18272# 16 | # awaiting https://gitlab.kitware.com/cmake/cmake/-/issues/17904 17 | set(_SYSTEM SYSTEM) 18 | endif() 19 | if(${scope} STREQUAL "INTERFACE" OR ${scope} STREQUAL "PUBLIC") 20 | target_include_directories( 21 | ${target} 22 | ${_SYSTEM} 23 | ${scope} 24 | "$" 25 | "$") 26 | else() 27 | target_include_directories( 28 | ${target} 29 | ${_SYSTEM} 30 | ${scope} 31 | ${lib_include_dirs}) 32 | endif() 33 | endforeach() 34 | endforeach() 35 | 36 | endfunction() 37 | 38 | # Include the directories of a library target as system directories (which suppresses their warnings). 39 | function( 40 | target_include_system_library 41 | target 42 | scope 43 | lib) 44 | # check if this is a target 45 | if(TARGET ${lib}) 46 | get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES) 47 | if(lib_include_dirs) 48 | target_include_system_directories(${target} ${scope} ${lib_include_dirs}) 49 | else() 50 | message(TRACE "${lib} library does not have the INTERFACE_INCLUDE_DIRECTORIES property.") 51 | endif() 52 | endif() 53 | endfunction() 54 | 55 | # Link a library target as a system library (which suppresses its warnings). 56 | function( 57 | target_link_system_library 58 | target 59 | scope 60 | lib) 61 | # Include the directories in the library 62 | target_include_system_library(${target} ${scope} ${lib}) 63 | 64 | # Link the library 65 | target_link_libraries(${target} ${scope} ${lib}) 66 | endfunction() 67 | 68 | # Link multiple library targets as system libraries (which suppresses their warnings). 69 | function(target_link_system_libraries target) 70 | set(multiValueArgs INTERFACE PUBLIC PRIVATE) 71 | cmake_parse_arguments( 72 | ARG 73 | "" 74 | "" 75 | "${multiValueArgs}" 76 | ${ARGN}) 77 | 78 | foreach(scope IN ITEMS INTERFACE PUBLIC PRIVATE) 79 | foreach(lib IN LISTS ARG_${scope}) 80 | target_link_system_library(${target} ${scope} ${lib}) 81 | endforeach() 82 | endforeach() 83 | endfunction() 84 | -------------------------------------------------------------------------------- /cmake/Tests.cmake: -------------------------------------------------------------------------------- 1 | function(chains_enable_coverage project_name) 2 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 3 | target_compile_options(${project_name} INTERFACE --coverage -O0 -g) 4 | target_link_libraries(${project_name} INTERFACE --coverage) 5 | endif() 6 | endfunction() 7 | -------------------------------------------------------------------------------- /cmake/Utilities.cmake: -------------------------------------------------------------------------------- 1 | # find a subtring from a string by a given prefix such as VCVARSALL_ENV_START 2 | function( 3 | find_substring_by_prefix 4 | output 5 | prefix 6 | input) 7 | # find the prefix 8 | string(FIND "${input}" "${prefix}" prefix_index) 9 | if("${prefix_index}" STREQUAL "-1") 10 | message(SEND_ERROR "Could not find ${prefix} in ${input}") 11 | endif() 12 | # find the start index 13 | string(LENGTH "${prefix}" prefix_length) 14 | math(EXPR start_index "${prefix_index} + ${prefix_length}") 15 | 16 | string( 17 | SUBSTRING "${input}" 18 | "${start_index}" 19 | "-1" 20 | _output) 21 | set("${output}" 22 | "${_output}" 23 | PARENT_SCOPE) 24 | endfunction() 25 | 26 | # A function to set environment variables of CMake from the output of `cmd /c set` 27 | function(set_env_from_string env_string) 28 | # replace ; in paths with __sep__ so we can split on ; 29 | string( 30 | REGEX 31 | REPLACE ";" 32 | "__sep__" 33 | env_string_sep_added 34 | "${env_string}") 35 | 36 | # the variables are separated by \r?\n 37 | string( 38 | REGEX 39 | REPLACE "\r?\n" 40 | ";" 41 | env_list 42 | "${env_string_sep_added}") 43 | 44 | foreach(env_var ${env_list}) 45 | # split by = 46 | string( 47 | REGEX 48 | REPLACE "=" 49 | ";" 50 | env_parts 51 | "${env_var}") 52 | 53 | list(LENGTH env_parts env_parts_length) 54 | if("${env_parts_length}" EQUAL "2") 55 | # get the variable name and value 56 | list( 57 | GET 58 | env_parts 59 | 0 60 | env_name) 61 | list( 62 | GET 63 | env_parts 64 | 1 65 | env_value) 66 | 67 | # recover ; in paths 68 | string( 69 | REGEX 70 | REPLACE "__sep__" 71 | ";" 72 | env_value 73 | "${env_value}") 74 | 75 | # set env_name to env_value 76 | set(ENV{${env_name}} "${env_value}") 77 | 78 | # update cmake program path 79 | if("${env_name}" EQUAL "PATH") 80 | list(APPEND CMAKE_PROGRAM_PATH ${env_value}) 81 | endif() 82 | endif() 83 | endforeach() 84 | endfunction() 85 | 86 | function(get_all_targets var) 87 | set(targets) 88 | get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR}) 89 | set(${var} 90 | ${targets} 91 | PARENT_SCOPE) 92 | endfunction() 93 | 94 | function(get_all_installable_targets var) 95 | set(targets) 96 | get_all_targets(targets) 97 | foreach(_target ${targets}) 98 | get_target_property(_target_type ${_target} TYPE) 99 | if(NOT 100 | ${_target_type} 101 | MATCHES 102 | ".*LIBRARY|EXECUTABLE") 103 | list(REMOVE_ITEM targets ${_target}) 104 | endif() 105 | endforeach() 106 | set(${var} 107 | ${targets} 108 | PARENT_SCOPE) 109 | endfunction() 110 | 111 | macro(get_all_targets_recursive targets dir) 112 | get_property( 113 | subdirectories 114 | DIRECTORY ${dir} 115 | PROPERTY SUBDIRECTORIES) 116 | foreach(subdir ${subdirectories}) 117 | get_all_targets_recursive(${targets} ${subdir}) 118 | endforeach() 119 | 120 | get_property( 121 | current_targets 122 | DIRECTORY ${dir} 123 | PROPERTY BUILDSYSTEM_TARGETS) 124 | list(APPEND ${targets} ${current_targets}) 125 | endmacro() 126 | 127 | function(is_verbose var) 128 | if("CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "VERBOSE" 129 | OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "DEBUG" 130 | OR "CMAKE_MESSAGE_LOG_LEVEL" STREQUAL "TRACE") 131 | set(${var} 132 | ON 133 | PARENT_SCOPE) 134 | else() 135 | set(${var} 136 | OFF 137 | PARENT_SCOPE) 138 | endif() 139 | endfunction() 140 | -------------------------------------------------------------------------------- /cmake/VCEnvironment.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/Utilities.cmake") 2 | 3 | macro(detect_architecture) 4 | # detect the architecture 5 | string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" CMAKE_SYSTEM_PROCESSOR_LOWER) 6 | if(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86 OR CMAKE_SYSTEM_PROCESSOR_LOWER MATCHES "^i[3456]86$") 7 | set(VCVARSALL_ARCH x86) 8 | elseif( 9 | CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x64 10 | OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL x86_64 11 | OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL amd64) 12 | set(VCVARSALL_ARCH x64) 13 | elseif(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm) 14 | set(VCVARSALL_ARCH arm) 15 | elseif(CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL arm64 OR CMAKE_SYSTEM_PROCESSOR_LOWER STREQUAL aarch64) 16 | set(VCVARSALL_ARCH arm64) 17 | else() 18 | if(CMAKE_HOST_SYSTEM_PROCESSOR) 19 | set(VCVARSALL_ARCH ${CMAKE_HOST_SYSTEM_PROCESSOR}) 20 | else() 21 | set(VCVARSALL_ARCH x64) 22 | message(STATUS "Unkown architecture CMAKE_SYSTEM_PROCESSOR: ${CMAKE_SYSTEM_PROCESSOR_LOWER} - using x64") 23 | endif() 24 | endif() 25 | endmacro() 26 | 27 | # Run vcvarsall.bat and set CMake environment variables 28 | function(run_vcvarsall) 29 | # if MSVC but VSCMD_VER is not set, which means vcvarsall has not run 30 | if(MSVC AND "$ENV{VSCMD_VER}" STREQUAL "") 31 | 32 | # find vcvarsall.bat 33 | get_filename_component(MSVC_DIR ${CMAKE_CXX_COMPILER} DIRECTORY) 34 | find_file( 35 | VCVARSALL_FILE 36 | NAMES vcvarsall.bat 37 | PATHS "${MSVC_DIR}" 38 | "${MSVC_DIR}/.." 39 | "${MSVC_DIR}/../.." 40 | "${MSVC_DIR}/../../../../../../../.." 41 | "${MSVC_DIR}/../../../../../../.." 42 | PATH_SUFFIXES "VC/Auxiliary/Build" "Common7/Tools" "Tools") 43 | 44 | if(EXISTS ${VCVARSALL_FILE}) 45 | # detect the architecture 46 | detect_architecture() 47 | 48 | # run vcvarsall and print the environment variables 49 | message(STATUS "Running `${VCVARSALL_FILE} ${VCVARSALL_ARCH}` to set up the MSVC environment") 50 | execute_process( 51 | COMMAND 52 | "cmd" "/c" ${VCVARSALL_FILE} ${VCVARSALL_ARCH} # 53 | "&&" "call" "echo" "VCVARSALL_ENV_START" # 54 | "&" "set" # 55 | OUTPUT_VARIABLE VCVARSALL_OUTPUT 56 | OUTPUT_STRIP_TRAILING_WHITESPACE) 57 | 58 | # parse the output and get the environment variables string 59 | find_substring_by_prefix(VCVARSALL_ENV "VCVARSALL_ENV_START" "${VCVARSALL_OUTPUT}") 60 | 61 | # set the environment variables 62 | set_env_from_string("${VCVARSALL_ENV}") 63 | 64 | else() 65 | message( 66 | WARNING 67 | "Could not find `vcvarsall.bat` for automatic MSVC environment preparation. Please manually open the MSVC command prompt and rebuild the project. 68 | ") 69 | endif() 70 | endif() 71 | endfunction() 72 | -------------------------------------------------------------------------------- /cmake/_FORTIFY_SOURCE.hpp: -------------------------------------------------------------------------------- 1 | #ifdef _FORTIFY_SOURCE 2 | #if _FORTIFY_SOURCE < 3 3 | #undef _FORTIFY_SOURCE 4 | #define _FORTIFY_SOURCE 3 5 | #endif 6 | #else 7 | #define _FORTIFY_SOURCE 3 8 | #endif 9 | -------------------------------------------------------------------------------- /configured_files/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # A very simple example of a configured file that might need to be 3 | # converted to one that is publicly installed in the case that 4 | # you are developing a library 5 | configure_file("config.hpp.in" "${CMAKE_BINARY_DIR}/configured_files/include/internal_use_only/config.hpp" ESCAPE_QUOTES) 6 | 7 | 8 | -------------------------------------------------------------------------------- /configured_files/config.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef chains_CONFIG_HPP 2 | #define chains_CONFIG_HPP 3 | 4 | // this is a basic example of how a CMake configured file might look 5 | // in this particular case, we are using it to set the version number of our executable 6 | namespace chains::cmake { 7 | inline constexpr std::string_view project_name = "@PROJECT_NAME@"; 8 | inline constexpr std::string_view project_version = "@PROJECT_VERSION@"; 9 | inline constexpr int project_version_major { @PROJECT_VERSION_MAJOR@ }; 10 | inline constexpr int project_version_minor { @PROJECT_VERSION_MINOR@ }; 11 | inline constexpr int project_version_patch { @PROJECT_VERSION_PATCH@ }; 12 | inline constexpr int project_version_tweak { @PROJECT_VERSION_TWEAK@ }; 13 | inline constexpr std::string_view git_sha = "@GIT_SHA@"; 14 | }// namespace chains::cmake 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /fuzz_test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # A fuzz test runs until it finds an error. This particular one is going to rely on libFuzzer. 2 | # 3 | 4 | find_package(fmt) 5 | 6 | add_executable(fuzz_tester fuzz_tester.cpp) 7 | target_link_libraries( 8 | fuzz_tester 9 | PRIVATE chains_options 10 | chains_warnings 11 | fmt::fmt 12 | -coverage 13 | -fsanitize=fuzzer) 14 | target_compile_options(fuzz_tester PRIVATE -fsanitize=fuzzer) 15 | 16 | # Allow short runs during automated testing to see if something new breaks 17 | set(FUZZ_RUNTIME 18 | 10 19 | CACHE STRING "Number of seconds to run fuzz tests during ctest run") # Default of 10 seconds 20 | 21 | add_test(NAME fuzz_tester_run COMMAND fuzz_tester -max_total_time=${FUZZ_RUNTIME}) 22 | -------------------------------------------------------------------------------- /fuzz_test/fuzz_tester.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | [[nodiscard]] auto sum_values(const uint8_t* Data, size_t Size) { 6 | constexpr auto scale = 1000; 7 | 8 | int value = 0; 9 | for (std::size_t offset = 0; offset < Size; ++offset) { 10 | value += static_cast(*std::next(Data, static_cast(offset))) * scale; 11 | } 12 | return value; 13 | } 14 | 15 | // Fuzzer that attempts to invoke undefined behavior for signed integer overflow 16 | // cppcheck-suppress unusedFunction symbolName=LLVMFuzzerTestOneInput 17 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) { 18 | fmt::print("Value sum: {}, len{}\n", sum_values(Data, Size), Size); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /include/chains/sample_library.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SAMPLE_LIBRARY_HPP 2 | #define SAMPLE_LIBRARY_HPP 3 | 4 | #include 5 | 6 | [[nodiscard]] SAMPLE_LIBRARY_EXPORT int factorial(int) noexcept; 7 | 8 | [[nodiscard]] constexpr int factorial_constexpr(int input) noexcept { 9 | if (input == 0) { 10 | return 1; 11 | } 12 | 13 | return input * factorial_constexpr(input - 1); 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/chains/tuple.hpp: -------------------------------------------------------------------------------- 1 | #include // std::apply, std::forward_as_tuple, std::tuple 2 | #include // std::is_same_v 3 | #include // std::forward, std::move 4 | #include // std::monostate 5 | 6 | #ifndef CHAIN_TUPLE_HPP 7 | #define CHAIN_TUPLE_HPP 8 | 9 | namespace chains::inline v0 { 10 | 11 | namespace detail { 12 | 13 | /* 14 | Operators for fold expression for sequential execution. Simpler way? 15 | */ 16 | 17 | template 18 | inline auto void_to_monostate(F& f) { 19 | return [&_f = f](auto&&... args) mutable { 20 | if constexpr (std::is_same_v(args)...)), 21 | void>) { 22 | std::move(_f)(std::forward(args)...); 23 | return std::monostate{}; 24 | } else { 25 | return std::move(_f)(std::forward(args)...); 26 | } 27 | }; 28 | } 29 | 30 | template 31 | struct tuple_pipeable { 32 | T _value; 33 | tuple_pipeable(T&& a) : _value{std::move(a)} {} 34 | }; 35 | 36 | template 37 | auto operator|(tuple_pipeable&& p, F& f) { 38 | return tuple_pipeable{void_to_monostate(f)(std::move(p._value))}; 39 | } 40 | 41 | } // namespace detail 42 | 43 | //-------------------------------------------------------------------------------------------------- 44 | template 45 | auto tuple_compose(std::tuple&& sequence) { 46 | return [_sequence = std::move(sequence)](auto&&... args) mutable { 47 | return std::move(std::apply( 48 | [_args = std::forward_as_tuple(std::forward(args)...)]( 49 | auto& first, auto&... functions) mutable { 50 | return ( 51 | detail::tuple_pipeable{std::apply(first, std::move(_args))} | 52 | ... | functions); 53 | }, 54 | _sequence) 55 | ._value); 56 | }; 57 | } 58 | 59 | //-------------------------------------------------------------------------------------------------- 60 | 61 | } // namespace chains::inline v0 62 | 63 | //-------------------------------------------------------------------------------------------------- 64 | 65 | #endif // CHAIN_TUPLE_HPP 66 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sample_library) 2 | add_subdirectory(ftxui_sample) 3 | -------------------------------------------------------------------------------- /src/ftxui_sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(intro main.cpp) 2 | 3 | target_link_libraries( 4 | intro 5 | PRIVATE chains::chains_options 6 | chains::chains_warnings) 7 | 8 | target_link_system_libraries( 9 | intro 10 | PRIVATE 11 | CLI11::CLI11 12 | fmt::fmt 13 | spdlog::spdlog 14 | lefticus::tools 15 | ftxui::screen 16 | ftxui::dom 17 | ftxui::component) 18 | 19 | target_include_directories(intro PRIVATE "${CMAKE_BINARY_DIR}/configured_files/include") 20 | -------------------------------------------------------------------------------- /src/ftxui_sample/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include // for ftxui 10 | #include // for Slider 11 | #include // for ScreenInteractive 12 | #include 13 | 14 | #include 15 | 16 | // This file will be generated automatically when cur_you run the CMake 17 | // configuration step. It creates a namespace called `chains`. You can modify 18 | // the source template at `configured_files/config.hpp.in`. 19 | #include 20 | 21 | template 22 | struct GameBoard { 23 | static constexpr std::size_t width = Width; 24 | static constexpr std::size_t height = Height; 25 | 26 | std::array, width> strings; 27 | std::array, width> values{}; 28 | 29 | std::size_t move_count{0}; 30 | 31 | std::string& get_string(std::size_t cur_x, std::size_t cur_y) { 32 | return strings.at(cur_x).at(cur_y); 33 | } 34 | 35 | void set(std::size_t cur_x, std::size_t cur_y, bool new_value) { 36 | get(cur_x, cur_y) = new_value; 37 | 38 | if (new_value) { 39 | get_string(cur_x, cur_y) = " ON"; 40 | } else { 41 | get_string(cur_x, cur_y) = "OFF"; 42 | } 43 | } 44 | 45 | void visit(auto visitor) { 46 | for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { 47 | for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { 48 | visitor(cur_x, cur_y, *this); 49 | } 50 | } 51 | } 52 | 53 | [[nodiscard]] bool get(std::size_t cur_x, std::size_t cur_y) const { 54 | return values.at(cur_x).at(cur_y); 55 | } 56 | 57 | [[nodiscard]] bool& get(std::size_t cur_x, std::size_t cur_y) { 58 | return values.at(cur_x).at(cur_y); 59 | } 60 | 61 | GameBoard() { 62 | visit([](const auto cur_x, const auto cur_y, auto& gameboard) { 63 | gameboard.set(cur_x, cur_y, true); 64 | }); 65 | } 66 | 67 | void update_strings() { 68 | for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { 69 | for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { 70 | set(cur_x, cur_y, get(cur_x, cur_y)); 71 | } 72 | } 73 | } 74 | 75 | void toggle(std::size_t cur_x, std::size_t cur_y) { set(cur_x, cur_y, !get(cur_x, cur_y)); } 76 | 77 | void press(std::size_t cur_x, std::size_t cur_y) { 78 | ++move_count; 79 | toggle(cur_x, cur_y); 80 | if (cur_x > 0) { 81 | toggle(cur_x - 1, cur_y); 82 | } 83 | if (cur_y > 0) { 84 | toggle(cur_x, cur_y - 1); 85 | } 86 | if (cur_x < width - 1) { 87 | toggle(cur_x + 1, cur_y); 88 | } 89 | if (cur_y < height - 1) { 90 | toggle(cur_x, cur_y + 1); 91 | } 92 | } 93 | 94 | [[nodiscard]] bool solved() const { 95 | for (std::size_t cur_x = 0; cur_x < width; ++cur_x) { 96 | for (std::size_t cur_y = 0; cur_y < height; ++cur_y) { 97 | if (!get(cur_x, cur_y)) { 98 | return false; 99 | } 100 | } 101 | } 102 | 103 | return true; 104 | } 105 | }; 106 | 107 | void consequence_game() { 108 | auto screen = ftxui::ScreenInteractive::TerminalOutput(); 109 | 110 | GameBoard<3, 3> game_board; 111 | 112 | std::string quit_text; 113 | 114 | const auto update_quit_text = [&quit_text](const auto& game_board_param) { 115 | quit_text = fmt::format("Quit ({} moves)", game_board_param.move_count); 116 | if (game_board_param.solved()) { 117 | quit_text += " Solved!"; 118 | } 119 | }; 120 | 121 | const auto make_buttons = [&] { 122 | std::vector buttons; 123 | for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { 124 | for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { 125 | buttons.push_back( 126 | ftxui::Button(&game_board.get_string(cur_x, cur_y), [=, &game_board] { 127 | if (!game_board.solved()) { 128 | game_board.press(cur_x, cur_y); 129 | } 130 | update_quit_text(game_board); 131 | })); 132 | } 133 | } 134 | return buttons; 135 | }; 136 | 137 | auto buttons = make_buttons(); 138 | 139 | auto quit_button = ftxui::Button(&quit_text, screen.ExitLoopClosure()); 140 | 141 | auto make_layout = [&] { 142 | std::vector rows; 143 | 144 | std::size_t idx = 0; 145 | 146 | for (std::size_t cur_x = 0; cur_x < game_board.width; ++cur_x) { 147 | std::vector row; 148 | for (std::size_t cur_y = 0; cur_y < game_board.height; ++cur_y) { 149 | row.push_back(buttons[idx]->Render()); 150 | ++idx; 151 | } 152 | rows.push_back(ftxui::hbox(std::move(row))); 153 | } 154 | 155 | rows.push_back(ftxui::hbox({quit_button->Render()})); 156 | 157 | return ftxui::vbox(std::move(rows)); 158 | }; 159 | 160 | static constexpr int randomization_iterations = 100; 161 | static constexpr int random_seed = 42; 162 | 163 | std::mt19937 gen32{random_seed}; // NOLINT fixed seed 164 | 165 | // NOLINTNEXTLINE This cannot be const 166 | std::uniform_int_distribution cur_x(static_cast(0), 167 | game_board.width - 1); 168 | // NOLINTNEXTLINE This cannot be const 169 | std::uniform_int_distribution cur_y(static_cast(0), 170 | game_board.height - 1); 171 | 172 | for (int i = 0; i < randomization_iterations; ++i) { 173 | game_board.press(cur_x(gen32), cur_y(gen32)); 174 | } 175 | game_board.move_count = 0; 176 | update_quit_text(game_board); 177 | 178 | auto all_buttons = buttons; 179 | all_buttons.push_back(quit_button); 180 | auto container = ftxui::Container::Horizontal(all_buttons); 181 | 182 | auto renderer = ftxui::Renderer(container, make_layout); 183 | 184 | screen.Loop(renderer); 185 | } 186 | 187 | struct Color { 188 | lefticus::tools::uint_np8_t R{static_cast(0)}; 189 | lefticus::tools::uint_np8_t G{static_cast(0)}; 190 | lefticus::tools::uint_np8_t B{static_cast(0)}; 191 | }; 192 | 193 | // A simple way of representing a bitmap on screen using only characters 194 | struct Bitmap : ftxui::Node { 195 | Bitmap(std::size_t width, 196 | std::size_t height) // NOLINT same typed parameters adjacent to each other 197 | : width_(width), height_(height) {} 198 | 199 | Color& at(std::size_t cur_x, std::size_t cur_y) { return pixels.at(width_ * cur_y + cur_x); } 200 | 201 | void ComputeRequirement() override { 202 | requirement_ = ftxui::Requirement{.min_x = static_cast(width_), 203 | .min_y = static_cast(height_ / 2), 204 | .selected_box{0, 0, 0, 0}}; 205 | } 206 | 207 | void Render(ftxui::Screen& screen) override { 208 | for (std::size_t cur_x = 0; cur_x < width_; ++cur_x) { 209 | for (std::size_t cur_y = 0; cur_y < height_ / 2; ++cur_y) { 210 | auto& pixel = screen.PixelAt(box_.x_min + static_cast(cur_x), 211 | box_.y_min + static_cast(cur_y)); 212 | pixel.character = "▄"; 213 | const auto& top_color = at(cur_x, cur_y * 2); 214 | const auto& bottom_color = at(cur_x, cur_y * 2 + 1); 215 | pixel.background_color = 216 | ftxui::Color{top_color.R.get(), top_color.G.get(), top_color.B.get()}; 217 | pixel.foreground_color = 218 | ftxui::Color{bottom_color.R.get(), bottom_color.G.get(), bottom_color.B.get()}; 219 | } 220 | } 221 | } 222 | 223 | [[nodiscard]] auto width() const noexcept { return width_; } 224 | 225 | [[nodiscard]] auto height() const noexcept { return height_; } 226 | 227 | [[nodiscard]] auto& data() noexcept { return pixels; } 228 | 229 | private: 230 | std::size_t width_; 231 | std::size_t height_; 232 | 233 | std::vector pixels = std::vector(width_ * height_, Color{}); 234 | }; 235 | 236 | void game_iteration_canvas() { 237 | // this should probably have a `bitmap` helper function that does what cur_you expect 238 | // similar to the other parts of FTXUI 239 | auto bm = std::make_shared(50, 50); // NOLINT magic numbers 240 | auto small_bm = std::make_shared(6, 6); // NOLINT magic numbers 241 | 242 | double fps = 0; 243 | 244 | std::size_t max_row = 0; 245 | std::size_t max_col = 0; 246 | 247 | // to do, add total game time clock also, not just current elapsed time 248 | auto game_iteration = [&](const std::chrono::steady_clock::duration elapsed_time) { 249 | // in here we simulate however much game time has elapsed. Update animations, 250 | // run character AI, whatever, update stats, etc 251 | 252 | // this isn't actually timing based for now, it's just updating the display however fast it 253 | // can 254 | fps = 1.0 / 255 | (static_cast( 256 | std::chrono::duration_cast(elapsed_time).count()) / 257 | 1'000'000.0); // NOLINT magic numbers 258 | 259 | for (std::size_t row = 0; row < max_row; ++row) { 260 | for (std::size_t col = 0; col < bm->width(); ++col) { 261 | ++(bm->at(col, row).R); 262 | } 263 | } 264 | 265 | for (std::size_t row = 0; row < bm->height(); ++row) { 266 | for (std::size_t col = 0; col < max_col; ++col) { 267 | ++(bm->at(col, row).G); 268 | } 269 | } 270 | 271 | // for the fun of it, let's have a second window doing interesting things 272 | auto& small_bm_pixel = small_bm->data().at(static_cast(elapsed_time.count()) % 273 | small_bm->data().size()); 274 | 275 | switch (elapsed_time.count() % 3) { 276 | case 0: 277 | small_bm_pixel.R += 11; // NOLINT Magic Number 278 | break; 279 | case 1: 280 | small_bm_pixel.G += 11; // NOLINT Magic Number 281 | break; 282 | case 2: 283 | small_bm_pixel.B += 11; // NOLINT Magic Number 284 | break; 285 | } 286 | 287 | ++max_row; 288 | if (max_row >= bm->height()) { 289 | max_row = 0; 290 | } 291 | ++max_col; 292 | if (max_col >= bm->width()) { 293 | max_col = 0; 294 | } 295 | }; 296 | 297 | auto screen = ftxui::ScreenInteractive::TerminalOutput(); 298 | 299 | int counter = 0; 300 | 301 | auto last_time = std::chrono::steady_clock::now(); 302 | 303 | auto make_layout = [&] { 304 | // This code actually processes the draw event 305 | const auto new_time = std::chrono::steady_clock::now(); 306 | 307 | ++counter; 308 | // we will dispatch to the game_iteration function, where the work happens 309 | game_iteration(new_time - last_time); 310 | last_time = new_time; 311 | 312 | // now actually draw the game elements 313 | return ftxui::hbox( 314 | {bm | ftxui::border, 315 | ftxui::vbox({ftxui::text("Frame: " + std::to_string(counter)), 316 | ftxui::text("FPS: " + std::to_string(fps)), small_bm | ftxui::border})}); 317 | }; 318 | 319 | auto renderer = ftxui::Renderer(make_layout); 320 | 321 | std::atomic refresh_ui_continue = true; 322 | 323 | // This thread exists to make sure that the event queue has an event to 324 | // process at approximately a rate of 30 FPS 325 | std::thread refresh_ui([&] { 326 | while (refresh_ui_continue) { 327 | using namespace std::chrono_literals; 328 | std::this_thread::sleep_for(1.0s / 30.0); // NOLINT magic numbers 329 | screen.PostEvent(ftxui::Event::Custom); 330 | } 331 | }); 332 | 333 | screen.Loop(renderer); 334 | 335 | refresh_ui_continue = false; 336 | refresh_ui.join(); 337 | } 338 | 339 | // NOLINTNEXTLINE(bugprone-exception-escape) 340 | int main(int argc, const char** argv) { 341 | try { 342 | CLI::App app{fmt::format("{} version {}", chains::cmake::project_name, 343 | chains::cmake::project_version)}; 344 | 345 | std::optional message; 346 | app.add_option("-m,--message", message, "A message to print back out"); 347 | bool show_version = false; 348 | app.add_flag("--version", show_version, "Show version information"); 349 | 350 | bool is_turn_based = false; 351 | auto* turn_based = app.add_flag("--turn_based", is_turn_based); 352 | 353 | bool is_loop_based = false; 354 | auto* loop_based = app.add_flag("--loop_based", is_loop_based); 355 | 356 | turn_based->excludes(loop_based); 357 | loop_based->excludes(turn_based); 358 | 359 | CLI11_PARSE(app, argc, argv); 360 | 361 | if (show_version) { 362 | fmt::print("{}\n", chains::cmake::project_version); 363 | return EXIT_SUCCESS; 364 | } 365 | 366 | if (is_turn_based) { 367 | consequence_game(); 368 | } else { 369 | game_iteration_canvas(); 370 | } 371 | 372 | } catch (const std::exception& e) { 373 | spdlog::error("Unhandled exception in main: {}", e.what()); 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /src/sample_library/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(GenerateExportHeader) 2 | 3 | 4 | add_library(sample_library sample_library.cpp) 5 | 6 | 7 | 8 | add_library(chains::sample_library ALIAS sample_library) 9 | 10 | target_link_libraries(sample_library PRIVATE chains_options chains_warnings) 11 | 12 | target_include_directories(sample_library ${WARNING_GUARD} PUBLIC $ 13 | $) 14 | 15 | target_compile_features(sample_library PUBLIC cxx_std_20) 16 | 17 | set_target_properties( 18 | sample_library 19 | PROPERTIES VERSION ${PROJECT_VERSION} 20 | CXX_VISIBILITY_PRESET hidden 21 | VISIBILITY_INLINES_HIDDEN YES) 22 | 23 | generate_export_header(sample_library EXPORT_FILE_NAME ${PROJECT_BINARY_DIR}/include/chains/sample_library_export.hpp) 24 | 25 | if(NOT BUILD_SHARED_LIBS) 26 | target_compile_definitions(sample_library PUBLIC SAMPLE_LIBRARY_STATIC_DEFINE) 27 | endif() 28 | -------------------------------------------------------------------------------- /src/sample_library/sample_library.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int factorial(int input) noexcept { 4 | int result = 1; 5 | 6 | while (input > 0) { 7 | result *= input; 8 | --input; 9 | } 10 | 11 | return result; 12 | } 13 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15...3.23) 2 | 3 | project(CmakeConfigPackageTests LANGUAGES CXX) 4 | 5 | # ---- Test as standalone project the exported config package ---- 6 | 7 | if(PROJECT_IS_TOP_LEVEL OR TEST_INSTALLED_VERSION) 8 | enable_testing() 9 | 10 | find_package(chains CONFIG REQUIRED) # for intro, project_options, ... 11 | 12 | if(NOT TARGET chains_options) 13 | message(FATAL_ERROR "Requiered config package not found!") 14 | return() # be strictly paranoid for Template Janitor github action! CK 15 | endif() 16 | endif() 17 | 18 | # ---- Dependencies ---- 19 | 20 | include(${Catch2_SOURCE_DIR}/extras/Catch.cmake) 21 | 22 | # Provide a simple smoke test to make sure that the CLI works and can display a --help message 23 | add_test(NAME cli.has_help COMMAND intro --help) 24 | 25 | # Provide a test to verify that the version being reported from the application 26 | # matches the version given to CMake. This will be important once you package 27 | # your program. Real world shows that this is the kind of simple mistake that is easy 28 | # to make, but also easy to test for. 29 | add_test(NAME cli.version_matches COMMAND intro --version) 30 | set_tests_properties(cli.version_matches PROPERTIES PASS_REGULAR_EXPRESSION "${PROJECT_VERSION}") 31 | 32 | add_executable(tests tuple_tests.cpp initial_draft.cpp) 33 | target_link_libraries( 34 | tests 35 | PRIVATE chains::chains_warnings 36 | chains::chains_options 37 | chains::sample_library 38 | stlab::stlab 39 | Catch2::Catch2WithMain) 40 | 41 | if(WIN32 AND BUILD_SHARED_LIBS) 42 | add_custom_command( 43 | TARGET tests 44 | PRE_BUILD 45 | COMMAND ${CMAKE_COMMAND} -E copy $ $ 46 | COMMAND_EXPAND_LISTS) 47 | endif() 48 | 49 | # automatically discover tests that are defined in catch based test files you can modify the unittests. Set TEST_PREFIX 50 | # to whatever you want, or use different for different binaries 51 | catch_discover_tests( 52 | tests 53 | TEST_PREFIX 54 | "unittests." 55 | REPORTER 56 | XML 57 | OUTPUT_DIR 58 | . 59 | OUTPUT_PREFIX 60 | "unittests." 61 | OUTPUT_SUFFIX 62 | .xml) 63 | 64 | # Add a file containing a set of constexpr tests 65 | # add_executable(constexpr_tests constexpr_tests.cpp) 66 | # target_link_libraries( 67 | # constexpr_tests 68 | # PRIVATE chains::chains_warnings 69 | # chains::chains_options 70 | # chains::sample_library 71 | # Catch2::Catch2WithMain) 72 | 73 | # catch_discover_tests( 74 | # constexpr_tests 75 | # TEST_PREFIX 76 | # "constexpr." 77 | # REPORTER 78 | # XML 79 | # OUTPUT_DIR 80 | # . 81 | # OUTPUT_PREFIX 82 | # "constexpr." 83 | # OUTPUT_SUFFIX 84 | # .xml) 85 | 86 | # Disable the constexpr portion of the test, and build again this allows us to have an executable that we can debug when 87 | # things go wrong with the constexpr testing 88 | # add_executable(relaxed_constexpr_tests constexpr_tests.cpp) 89 | # target_link_libraries( 90 | # relaxed_constexpr_tests 91 | # PRIVATE chains::chains_warnings 92 | # chains::chains_options 93 | # chains::sample_library 94 | # Catch2::Catch2WithMain) 95 | # target_compile_definitions(relaxed_constexpr_tests PRIVATE -DCATCH_CONFIG_RUNTIME_STATIC_REQUIRE) 96 | 97 | # catch_discover_tests( 98 | # relaxed_constexpr_tests 99 | # TEST_PREFIX 100 | # "relaxed_constexpr." 101 | # REPORTER 102 | # XML 103 | # OUTPUT_DIR 104 | # . 105 | # OUTPUT_PREFIX 106 | # "relaxed_constexpr." 107 | # OUTPUT_SUFFIX 108 | # .xml) 109 | -------------------------------------------------------------------------------- /test/initial_draft.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | // temporary 12 | #include 13 | #include 14 | #include 15 | 16 | #define STLAB_FWD(x) std::forward(x) 17 | 18 | /* 19 | 20 | If exception inside of a segment _apply_ function throws an exception then the exception must be 21 | set on the receiver. 22 | 23 | */ 24 | 25 | namespace chains::inline v1 { 26 | 27 | /* 28 | segment is invoked with a receiver - 29 | 30 | 31 | 32 | */ 33 | 34 | template 35 | class segment { 36 | std::tuple _functions; 37 | Applicator _apply; 38 | 39 | public: 40 | template 41 | auto result_type_helper(Args&&... args) && { 42 | return tuple_compose(std::move(_functions))(std::forward(args)...); 43 | } 44 | 45 | explicit segment(Applicator&& apply, std::tuple&& functions) 46 | : _functions{std::move(functions)}, _apply{std::move(apply)} {} 47 | explicit segment(Applicator&& apply, Fs&&... functions) 48 | : _functions{std::move(functions)...}, _apply{std::move(apply)} {} 49 | 50 | /* 51 | The basic operations should follow those from C++ lambdas, for now default everything. 52 | and see if the compiler gets it correct. 53 | */ 54 | explicit segment(const segment&) = default; 55 | segment(segment&&) noexcept = default; 56 | segment& operator=(const segment&) = default; 57 | segment& operator=(segment&&) noexcept = default; 58 | 59 | template 60 | auto append(F&& f) && { 61 | return chains::segment{std::move(_apply), std::tuple_cat(std::move(_functions), 62 | std::tuple{std::forward(f)})}; 63 | } 64 | 65 | #if 0 66 | template 67 | auto operator()(Args&&... args) && /* const/non-const version? - noexcept(...) */ { 68 | return std::move(_apply)(compose_tuple(std::move(_functions)), std::forward(args)...); 69 | } 70 | #endif 71 | /* 72 | The apply function for a segment always returns void. 73 | 74 | Invoke will check the receiver for cancelation - 75 | If not conceled, apply(segement), cancelation is checked before execution of the segment 76 | and any exception during the segment is propogated to the receiever. 77 | */ 78 | 79 | template 80 | void invoke(const R& receiver, Args&&... args) && { 81 | if (receiver.canceled()) return; 82 | 83 | std::move(_apply)( 84 | [_f = tuple_compose(std::move(_functions)), 85 | _receiver = receiver](auto&&... args) mutable noexcept { 86 | if (_receiver.canceled()) return; 87 | try { 88 | std::move(_f)(std::forward(args)...); 89 | } catch (...) { 90 | _receiver.set_exception(std::current_exception()); 91 | } 92 | }, 93 | std::forward(args)...); 94 | } 95 | }; 96 | 97 | namespace detail { 98 | 99 | /// Apply a recursive lambda to each element in the tuple-like Segments. 100 | template 101 | constexpr auto fold_over(Fold fold, Segments&& segments) { 102 | return std::apply([fold](auto&&... links) { return fold(fold, STLAB_FWD(links)...); }, 103 | STLAB_FWD(segments)); 104 | } 105 | 106 | } // namespace detail 107 | 108 | template 109 | using segment_result_type = 110 | decltype(std::declval().result_type_helper(std::declval()...)); 111 | 112 | /* 113 | simplify this code by handing the multi-argument case earlier (somehow). 114 | */ 115 | 116 | template 117 | class chain { 118 | Tail _tail; 119 | segment _head; 120 | 121 | /// Return a lambda with the signature of 122 | /// head( tail( tail<1>( tail<0>( auto&& args... ) ) ) ) 123 | /// for computing the result type of this chain. 124 | static consteval auto result_type_helper(Tail&& tail, segment&& head) { 125 | return detail::fold_over( 126 | [](auto fold, auto&& first, auto&&... rest) { 127 | if constexpr (sizeof...(rest) == 0) { 128 | return [_segment = STLAB_FWD(first)](auto&&... args) mutable { 129 | return std::move(_segment).result_type_helper(STLAB_FWD(args)...); 130 | }; 131 | } else { 132 | return [_segment = STLAB_FWD(first).append(fold(fold, STLAB_FWD(rest)...))]( 133 | auto&&... args) mutable { 134 | return std::move(_segment).result_type_helper(STLAB_FWD(args)...); 135 | }; 136 | } 137 | }, 138 | std::tuple_cat(std::move(tail), std::tuple{std::move(head)})); 139 | } 140 | 141 | template 142 | auto expand(const R& receiver) && { 143 | return detail::fold_over( 144 | [receiver](auto fold, auto&& first, auto&&... rest) { 145 | if constexpr (sizeof...(rest) == 0) { 146 | return [receiver, 147 | _segment = STLAB_FWD(first).append(receiver)](auto&&... args) mutable { 148 | return std::move(_segment).invoke(receiver, STLAB_FWD(args)...); 149 | }; 150 | } else { 151 | return [receiver, _segment = STLAB_FWD(first).append( 152 | fold(fold, STLAB_FWD(rest)...))](auto&&... args) mutable { 153 | return std::move(_segment).invoke(receiver, STLAB_FWD(args)...); 154 | }; 155 | } 156 | }, 157 | std::tuple_cat(std::move(_tail), std::tuple{std::move(_head)})); 158 | } 159 | 160 | public: 161 | template 162 | using result_type = decltype(result_type_helper( 163 | std::declval(), std::declval>())(std::declval()...)); 164 | 165 | explicit chain(Tail&& tail, segment&& head) 166 | : _tail{std::move(tail)}, _head{std::move(head)} {} 167 | 168 | /* 169 | The basic operations should follow those from C++ lambdas, for now default everything. 170 | and see if the compiler gets it correct. 171 | */ 172 | 173 | explicit chain(const chain&) = default; 174 | chain(chain&&) noexcept = default; 175 | chain& operator=(const chain&) = default; 176 | chain& operator=(chain&&) noexcept = default; 177 | 178 | // append function to the last sequence 179 | template 180 | auto append(F&& f) && { 181 | return chains::chain{std::move(_tail), std::move(_head).append(std::forward(f))}; 182 | } 183 | 184 | template 185 | auto append(segment&& head) && { 186 | return chains::chain{std::tuple_cat(std::move(_tail), std::make_tuple(std::move(_head))), 187 | std::move(head)}; 188 | } 189 | 190 | template 191 | auto operator()(Args&&... args) && { 192 | using result_t = result_type; 193 | auto [receiver, future] = 194 | stlab::package(stlab::immediate_executor, std::identity{}); 195 | (void)std::move(*this).expand(receiver)(std::forward(args)...); 196 | return std::move(future); 197 | } 198 | 199 | template 200 | friend auto operator|(chain&& c, F&& f) { 201 | return std::move(c).append(std::forward(f)); 202 | } 203 | 204 | template 205 | friend auto operator|(chain&& c, segment&& head) { 206 | return std::move(c).append(std::move(head)); 207 | } 208 | }; 209 | 210 | template 211 | chain(Tail&& tail, segment&& head) -> chain; 212 | 213 | template 214 | inline auto operator|(segment&& head, F&& f) { 215 | return chain{std::tuple<>{}, std::move(head).append(std::forward(f))}; 216 | } 217 | 218 | } // namespace chains::inline v1 219 | 220 | //-------------------------------------------------------------------------------------------------- 221 | 222 | #include 223 | #include 224 | 225 | namespace chains::inline v1 { 226 | 227 | #if 0 228 | template 229 | inline auto on(E&& executor) { 230 | return segment{[_executor = std::forward(executor)](auto&& f, auto&&... args) mutable { 231 | return stlab::async(std::move(_executor), std::forward(f), 232 | std::forward(args)...); 233 | }}; 234 | } 235 | #endif 236 | 237 | /* 238 | 239 | Each segment invokes the next segment with result and returns void. Promise is bound to the 240 | last item in the chain as a segment. 241 | 242 | */ 243 | template 244 | inline auto on(E&& executor) { 245 | return segment{[_executor = std::forward(executor)](auto&& f, auto&&... args) mutable { 246 | std::move(_executor)( 247 | [_f = std::forward(f), 248 | _args = std::tuple{std::forward(args)...}]() mutable noexcept { 249 | std::apply(std::move(_f), std::move(_args)); 250 | }); 251 | return std::monostate{}; 252 | }}; 253 | } 254 | 255 | #if 0 256 | 257 | 258 | /* 259 | TODO: The ergonimics of chains are painful with three arguements. We could reduce to a single 260 | argument or move to a concept? Here I really want the forward reference to be an rvalue ref. 261 | 262 | The implementation of sync_wait is complicated by the fact that the promise is currently hard/ 263 | wired into the chain. sync_wait needs to be able to invoke the promise/receiver - _then_ flag 264 | the condition that it is ready. 265 | */ 266 | 267 | 268 | template 269 | inline auto sync_wait(Chain&& chain) { 270 | /* 271 | TODO: (sean-parent) - we should have an invoke awaiting parameterized on what we are waiting 272 | The implementation of which would be used in stlab::await() and used here. With this 273 | construct we don't spin up more than one thread (hmm, maybe we shouldn't?). 274 | */ 275 | auto appended = std::forward(chain) | [&] 276 | invoke_awaiting( 277 | ); 278 | } 279 | #endif 280 | 281 | #if 0 282 | inline auto apply() { 283 | return segment{[](auto&& f, auto&&... args) { 284 | return std::forward(f)(std::forward(args)...); 285 | }}; 286 | } 287 | 288 | template 289 | inline auto then(F&& future) { 290 | return segment{[_future = std::forward(future)](auto&& f) { 291 | return std::move(_future).then(std::forward(f)); 292 | }}; 293 | } 294 | 295 | #endif 296 | 297 | } // namespace chains::inline v1 298 | 299 | //-------------------------------------------------------------------------------------------------- 300 | 301 | #include 302 | #include 303 | #include 304 | #include 305 | 306 | using namespace std; 307 | using namespace chains; 308 | using namespace stlab; 309 | 310 | TEST_CASE("Initial draft", "[initial_draft]") { 311 | auto a0 = on(default_executor) | [] { 312 | cout << "Hello from thread: " << std::this_thread::get_id() << "\n"; 313 | return 42; 314 | }; 315 | 316 | auto a1 = std::move(a0) | on(default_executor) | [](int x) { 317 | cout << "received: " << x << " on thread: " << std::this_thread::get_id() << "\n"; 318 | // throw std::runtime_error("test-exception"); 319 | return "forwarding: " + std::to_string(x + 1); 320 | }; 321 | 322 | cout << "Main thread: " << std::this_thread::get_id() << "\n"; 323 | cout << "Ready to go async!\n"; 324 | 325 | #if 0 326 | auto a2 = then(std::move(a1)()) | [](std::string s){ 327 | cout << s << "<-- \n"; 328 | return 0; 329 | }; 330 | #endif 331 | 332 | #if 0 333 | { 334 | auto f = std::move(a1)(); // start and cancel. 335 | std::this_thread::sleep_for(1ns); 336 | } 337 | #endif 338 | 339 | #if 0 340 | // TODO: (sean-parent) await on a chain can be optimized. 341 | 342 | try { 343 | std::cout << any_cast(await(std::move(a1)())) << "\n"; 344 | } catch(const std::exception& error) { 345 | std::cout << "exception: " << error.what() << "\n"; 346 | } 347 | #endif 348 | 349 | // std::this_thread::sleep_for(3s); 350 | 351 | std::cout << await(std::move(a1)()) << "\n"; 352 | 353 | pre_exit(); 354 | } 355 | -------------------------------------------------------------------------------- /test/tuple_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | TEST_CASE("Test tuple compose", "[tuple_compose]") { 12 | std::tuple t{[](int x) { return x + 1.0; }, [](double x) { return x * 2.0; }, 13 | [](double x) { return std::to_string(x / 2.0); }}; 14 | REQUIRE(chains::tuple_compose(std::move(t))(1) == "2.000000"); 15 | } 16 | --------------------------------------------------------------------------------