diff --git a/.github/workflows/cpm.yml b/.github/workflows/cpm.yml new file mode 100644 index 0000000..9f5b751 --- /dev/null +++ b/.github/workflows/cpm.yml @@ -0,0 +1,30 @@ +name: CPM + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: + +jobs: + test-cpm: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y cmake build-essential wget g++-14 + + - name: Run CPM test + run: | + export CC=gcc-14 + export CXX=g++-14 + cd tests/cpm + chmod +x test_cpm.sh + ./test_cpm.sh diff --git a/.gitignore b/.gitignore index 1933961..e351518 100644 --- a/.gitignore +++ b/.gitignore @@ -9,5 +9,8 @@ /tests/benchmarks/NamedType /tests/benchmarks/compile-time /tests/benchmarks/results.md +/tests/cpm/build/ +tests/cpm/cmake/ +tests/cpm/CMakeLists.txt CMakeUserPresets.json includes/version.hpp \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index da44c00..1efe991 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,138 +1,154 @@ cmake_minimum_required(VERSION 3.31) project(cpp_strong_type) -set(CMAKE_CXX_STANDARD 23) - -if (MSVC) - set(WARNING_FLAGS - /W4 - /WX - /permissive- - /Zc:__cplusplus - ) -else () - set(WARNING_FLAGS - -Wall - -Wextra - -Wpedantic - -Werror - -Wshadow - -Wnon-virtual-dtor - -Wold-style-cast - -Wcast-align - -Wunused - -Woverloaded-virtual - -Wpedantic - -Wconversion - -Wsign-conversion - -Wnull-dereference - -Wdouble-promotion - -Wformat=2 - -Wimplicit-fallthrough - ) - - if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - list(APPEND WARNING_FLAGS - -Wmisleading-indentation - -Wduplicated-cond - -Wduplicated-branches - -Wlogical-op - -Wuseless-cast - ) - endif () - - if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - list(APPEND WARNING_FLAGS - -Wc++23-compat - -Wmost - ) - endif () -endif () - -add_compile_options(${WARNING_FLAGS}) -find_package(Catch2 3 REQUIRED) - # Create version.hpp ___________________________________________________________________________________________________ add_custom_target(create_version_hpp - COMMAND ${CMAKE_COMMAND} -E env python3 ./version.py - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E env python3 ${CMAKE_CURRENT_SOURCE_DIR}/version.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating version.hpp" ) # Create single header library _________________________________________________________________________________________ add_custom_target(make_header_only - COMMAND ${CMAKE_COMMAND} -E env python3 ./make-header-only.py - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_COMMAND} -E env python3 ${CMAKE_CURRENT_SOURCE_DIR}/make-header-only.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Generating header-only library" ) add_dependencies(make_header_only create_version_hpp) -# Unit-tests target with Catch2 _______________________________________________________________________________________ - -enable_testing() - -# set include path in a variable -set(HEADERS_PATH ${CMAKE_SOURCE_DIR}/public) - -add_executable(stronger_cpp_tests - tests/test_link/common.cpp - tests/test_link/api1.cpp - tests/test_declaration_macros.cpp - tests/test_arithmetic.cpp - tests/helpers.h - tests/mock_type.hpp - tests/test_operation_bindings.cpp - tests/test_bool.cpp - tests/test_types.cpp - tests/test_strength.cpp - tests/test_link.cpp - tests/test_data_accessors.cpp - includes/tag.hpp - tests/test_tag.cpp - tests/test_iterable.cpp - tests/runtime_tests.cpp - tests/test_sizes.cpp - tests/test_make_strong.cpp - includes/concepts/other_operators.hpp - tests/test_options.cpp - tests/test_version.cpp -) -target_compile_definitions(stronger_cpp_tests - PRIVATE - STRONGER_CPP_BUILD_TESTS -) -add_dependencies(stronger_cpp_tests make_header_only) -target_include_directories(stronger_cpp_tests - PRIVATE - ${HEADERS_PATH} -) -target_link_libraries(stronger_cpp_tests - PRIVATE - Catch2::Catch2WithMain +# Library target for CPM consumers _____________________________________________________________________________________ + +add_library(stronger-cpp INTERFACE) +add_library(stronger-cpp::stronger-cpp ALIAS stronger-cpp) +add_dependencies(stronger-cpp make_header_only) +target_include_directories(stronger-cpp + INTERFACE + $ ) +target_compile_features(stronger-cpp INTERFACE cxx_std_23) -# Download NamedType for benchmarks ____________________________________________________________________________________ +# Local targets ________________________________________________________________________________________________________ -add_custom_target(download_NamedType - COMMAND ${CMAKE_COMMAND} -E env python3 ./tests/benchmarks/get-NamedType.py - WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} - COMMENT "Getting NamedType library for benchmarks" -) +if (PROJECT_IS_TOP_LEVEL) + set(CMAKE_CXX_STANDARD 23) + + if (MSVC) + set(WARNING_FLAGS + /W4 + /WX + /permissive- + /Zc:__cplusplus + ) + else () + set(WARNING_FLAGS + -Wall + -Wextra + -Wpedantic + -Werror + -Wshadow + -Wnon-virtual-dtor + -Wold-style-cast + -Wcast-align + -Wunused + -Woverloaded-virtual + -Wpedantic + -Wconversion + -Wsign-conversion + -Wnull-dereference + -Wdouble-promotion + -Wformat=2 + -Wimplicit-fallthrough + ) -# Runtime benchmarks ___________________________________________________________________________________________________ + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + list(APPEND WARNING_FLAGS + -Wmisleading-indentation + -Wduplicated-cond + -Wduplicated-branches + -Wlogical-op + -Wuseless-cast + ) + endif () + + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + list(APPEND WARNING_FLAGS + -Wc++23-compat + -Wmost + ) + endif () + endif () -add_executable(stronger_cpp_benchmarks - tests/benchmarks/benchmarks.cpp -) -add_dependencies(stronger_cpp_benchmarks make_header_only download_NamedType) -target_include_directories(stronger_cpp_benchmarks - PRIVATE - ${HEADERS_PATH} -) -target_link_libraries(stronger_cpp_benchmarks - PRIVATE - Catch2::Catch2WithMain -) \ No newline at end of file + add_compile_options(${WARNING_FLAGS}) + + # Unit-tests target with Catch2 _______________________________________________________________________________________ + + find_package(Catch2 3 REQUIRED) + enable_testing() + + # set include path in a variable + set(HEADERS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/includes) + + add_executable(stronger_cpp_tests + tests/test_link/common.cpp + tests/test_link/api1.cpp + tests/test_declaration_macros.cpp + tests/test_arithmetic.cpp + tests/helpers.h + tests/mock_type.hpp + tests/test_operation_bindings.cpp + tests/test_bool.cpp + tests/test_types.cpp + tests/test_strength.cpp + tests/test_link.cpp + tests/test_data_accessors.cpp + includes/tag.hpp + tests/test_tag.cpp + tests/test_iterable.cpp + tests/runtime_tests.cpp + tests/test_sizes.cpp + tests/test_make_strong.cpp + includes/concepts/other_operators.hpp + tests/test_options.cpp + tests/test_version.cpp + ) + target_compile_definitions(stronger_cpp_tests + PRIVATE + STRONGER_CPP_BUILD_TESTS + ) + add_dependencies(stronger_cpp_tests make_header_only) + target_include_directories(stronger_cpp_tests + PRIVATE + ${HEADERS_PATH} + ) + target_link_libraries(stronger_cpp_tests + PRIVATE + Catch2::Catch2WithMain + ) + + # Download NamedType for benchmarks ____________________________________________________________________________________ + + add_custom_target(download_NamedType + COMMAND ${CMAKE_COMMAND} -E env python3 ${CMAKE_CURRENT_SOURCE_DIR}/tests/benchmarks/get-NamedType.py + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Getting NamedType library for benchmarks" + ) + + # Runtime benchmarks ___________________________________________________________________________________________________ + + add_executable(stronger_cpp_benchmarks + tests/benchmarks/benchmarks.cpp + ) + add_dependencies(stronger_cpp_benchmarks make_header_only download_NamedType) + target_include_directories(stronger_cpp_benchmarks + PRIVATE + ${HEADERS_PATH} + ) + target_link_libraries(stronger_cpp_benchmarks + PRIVATE + Catch2::Catch2WithMain + ) + +endif () \ No newline at end of file diff --git a/readme.md b/readme.md index c5f5034..ffb5756 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,4 @@ -# stronger-cpp [![Build + Tests](https://github.com/Teskann/stronger-cpp/actions/workflows/ci.yml/badge.svg)](https://github.com/Teskann/stronger-cpp/actions/workflows/ci.yml) - +# stronger-cpp [![Build + Tests](https://github.com/Teskann/stronger-cpp/actions/workflows/ci.yml/badge.svg)](https://github.com/Teskann/stronger-cpp/actions/workflows/ci.yml) [![CPM](https://github.com/teskann/stronger-cpp/actions/workflows/cpm.yml/badge.svg)](https://github.com/teskann/stronger-cpp/actions/workflows/cpm.yml) ### Single-header C++23 strong typing library @@ -15,7 +14,7 @@ making it harder to accidentally mix or misuse variables in C++ code.* - ✅ No visible runtime overhead. [[see more]](#runtime) - ✅ Tested at compile time (strong guarantee against undefined behaviors) -# Table of Contents +## Table of Contents * [Usage](#usage) * [Basics](#basics) @@ -34,6 +33,9 @@ making it harder to accidentally mix or misuse variables in C++ code.* * [Compiling the library header](#compiling-the-library-header) * [Compiling C++ file](#compiling-this-cpp-file) * [Runtime](#runtime) +* [Get it](#get-it) + * [From release](#from-release) + * [CPM](#using-cpm) * [Build from source](#build-from-source) * [Debug](#debug) * [Release](#release) @@ -57,7 +59,7 @@ Once declared, you can use `Amount` and `Name` in your code: ```C++ void print_info(const Name& name, Amount amount) { - std::println("{} gave {.2f}$", amount); + std::println("{} gave {.2f}$", name, amount); } Name alice{"Alice"s}; @@ -386,6 +388,20 @@ is close to 1 ms. We can conclude that **there is no visible runtime overhead when using a strong typing library, no matter if it is NamedType or stronger-cpp**. +## Get it + +### Using CPM + +```cmake +# Get any version (here getting the latest commit on master) +CPMAddPackage("gh:teskann/stronger-cpp#master") +target_link_libraries(YOUR_TARGET PRIVATE stronger-cpp::stronger-cpp) +``` + +### From release + +You can download the latest release from [here](https://github.com/Teskann/stronger-cpp/releases/latest). + ## Build from source Clone the repository and make sure you have conan installed: diff --git a/tests/cpm/CMakeLists.txt.template b/tests/cpm/CMakeLists.txt.template new file mode 100644 index 0000000..7afe6ef --- /dev/null +++ b/tests/cpm/CMakeLists.txt.template @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.31) +project(cpmtest) + +include(cmake/CPM.cmake) +CPMAddPackage("gh:teskann/stronger-cpp#__COMMIT_PLACEHOLDER__") + +add_executable(cpmtest main.cpp) +target_link_libraries(cpmtest PRIVATE stronger-cpp::stronger-cpp) diff --git a/tests/cpm/main.cpp b/tests/cpm/main.cpp new file mode 100644 index 0000000..4c37474 --- /dev/null +++ b/tests/cpm/main.cpp @@ -0,0 +1,11 @@ +// Copyright (c) 2025 Clément Metz +// Licensed under the MIT License. See LICENSE file for details. + +#include +#include + +int main() { + stronger::strong_type a{ 1.0 }; + std::cout << "Hello, World!" << a << std::endl; + return 0; +} diff --git a/tests/cpm/test_cpm.sh b/tests/cpm/test_cpm.sh new file mode 100755 index 0000000..8fb96f9 --- /dev/null +++ b/tests/cpm/test_cpm.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +SCRIPT_DIR="$(dirname "$0")" +cd "$SCRIPT_DIR" || exit + +if [ -n "$GITHUB_HEAD_REF" ]; then + BRANCH_NAME="$GITHUB_HEAD_REF" +elif [ -n "$GITHUB_REF_NAME" ]; then + BRANCH_NAME="$GITHUB_REF_NAME" +else + BRANCH_NAME=$(git branch --show-current) +fi + +echo "$BRANCH_NAME" + +sed "s/__COMMIT_PLACEHOLDER__/$BRANCH_NAME/g" CMakeLists.txt.template > CMakeLists.txt + +mkdir -p cmake +wget -O cmake/CPM.cmake https://github.com/cpm-cmake/CPM.cmake/releases/latest/download/get_cpm.cmake +cmake -S . -B build +cmake --build build +./build/cpmtest