diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index 03768356..1526a2c5 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -35,12 +35,13 @@ env: jobs: build: - name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: formatting: ["std::format", "fmtlib"] + contracts: ["none", "gsl-lite", "ms-gsl"] std: [20, 23] config: # - { @@ -175,19 +176,20 @@ jobs: cache-name: cache-conan-data with: path: ~/.conan2/p - key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + key: conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} restore-keys: | - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- - conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + conan-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- conan-${{ matrix.config.os }}-${{ matrix.formatting }}- conan-${{ matrix.config.os }}- - uses: hendrikmuhs/ccache-action@v1.2 if: runner.os == 'Linux' with: - key: ${{ matrix.config.os }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} + key: ${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }} max-size: 50M - name: Install gcc-13 if: matrix.config.compiler.type == 'GCC' && matrix.config.compiler.version == '13' @@ -241,7 +243,7 @@ jobs: run: | conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \ -b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ - -c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} ${{ matrix.config.conan-config }} + -c user.mp-units.build:all=True -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} ${{ matrix.config.conan-config }} - name: Obtain package reference id: get-package-ref shell: bash diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 98abe43c..ea812567 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -36,12 +36,13 @@ on: jobs: test_package: - name: "${{ matrix.formatting }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" + name: "${{ matrix.formatting }} ${{ matrix.contracts }} C++${{ matrix.std }} ${{ matrix.config.name }} ${{ matrix.build_type }}" runs-on: ${{ matrix.config.os }} strategy: fail-fast: false matrix: formatting: ["std::format", "fmtlib"] + contracts: ["none", "gsl-lite", "ms-gsl"] std: [20, 23] config: # - { @@ -172,13 +173,14 @@ jobs: cache-name: cache-conan-data with: path: ~/.conan2/p - key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} + key: cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}-${{ env.cache_id }} restore-keys: | - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- - cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.config.compiler.type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}-${{ matrix.std }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}-${{ matrix.config.compiler.version }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}-${{ matrix.build_type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}-${{ matrix.config.lib }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}-${{ matrix.config.compiler.type }}- + cmake-${{ matrix.config.os }}-${{ matrix.formatting }}-${{ matrix.contracts }}- cmake-${{ matrix.config.os }}-${{ matrix.formatting }}- cmake-${{ matrix.config.os }}- - name: Install gcc-13 @@ -233,7 +235,7 @@ jobs: shell: bash run: | conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \ - -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} + -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} -o contracts=${{ matrix.contracts }} - name: Provide dependencies for the build shell: bash working-directory: src diff --git a/conanfile.py b/conanfile.py index 9f51a130..c7be62dd 100644 --- a/conanfile.py +++ b/conanfile.py @@ -65,12 +65,14 @@ class MPUnitsConan(ConanFile): "std_format": ["auto", True, False], "string_view_ret": ["auto", True, False], "no_crtp": ["auto", True, False], + "contracts": ["none", "gsl-lite", "ms-gsl"], } default_options = { "cxx_modules": "auto", "std_format": "auto", "string_view_ret": "auto", "no_crtp": "auto", + "contracts": "gsl-lite", } tool_requires = "cmake/[>=3.29]" implements = "auto_header_only" @@ -199,7 +201,10 @@ class MPUnitsConan(ConanFile): self.version = version.strip() def requirements(self): - self.requires("gsl-lite/0.41.0") + if self.options.contracts == "gsl-lite": + self.requires("gsl-lite/0.41.0") + elif self.options.contracts == "ms-gsl": + self.requires("ms-gsl/4.0.0") if self._use_fmtlib: self.requires("fmt/10.2.1") @@ -237,6 +242,9 @@ class MPUnitsConan(ConanFile): self.options.string_view_ret ).upper() tc.cache_variables["MP_UNITS_API_NO_CRTP"] = str(self.options.no_crtp).upper() + tc.cache_variables["MP_UNITS_API_CONTRACTS"] = str( + self.options.contracts + ).upper() tc.generate() deps = CMakeDeps(self) deps.generate() @@ -263,7 +271,10 @@ class MPUnitsConan(ConanFile): def package_info(self): compiler = self.settings.compiler - self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] + if self.options.contracts == "gsl-lite": + self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] + elif self.options.contracts == "ms-gsl": + self.cpp_info.components["core"].requires = ["ms-gsl::ms-gsl"] if self._use_fmtlib: self.cpp_info.components["core"].requires.append("fmt::fmt") if compiler == "msvc": diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index 7e623f56..e21dd6ce 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -18,8 +18,8 @@ projects: - in case this library becomes part of the C++ standard, it will have no external dependencies but until then, it depends on the following: - - [gsl-lite](https://github.com/gsl-lite/gsl-lite) to verify runtime contracts with - the `gsl_Expects` macro, + - [gsl-lite](https://github.com/gsl-lite/gsl-lite) or [ms-gsl](https://github.com/microsoft/GSL) + to verify runtime contracts (if contract checking is enabled), - [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities (if `std::format` is not supported yet on a specific compiler). @@ -229,7 +229,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} ### Conan options -[cxx_modules](#cxx_modules){ #cxx_modules } +[`cxx_modules`](#cxx_modules){ #cxx_modules } : [:octicons-tag-24: 2.2.0][conan C++ modules support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -237,7 +237,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan C++ modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[std_format](#std_format){ #std_format } +[`std_format`](#std_format){ #std_format } : [:octicons-tag-24: 2.2.0][conan std::format support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -247,7 +247,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[string_view_ret](#string_view_ret){ #string_view_ret } +[`string_view_ret`](#string_view_ret){ #string_view_ret } : [:octicons-tag-24: 2.2.0][conan returning string_view] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -259,7 +259,7 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 -[no_crtp](#no_crtp){ #no_crtp } +[`no_crtp`](#no_crtp){ #no_crtp } : [:octicons-tag-24: 2.2.0][conan no crtp support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`) @@ -268,6 +268,13 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [conan no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`contracts`](#contracts){ #contracts } + +: [:octicons-tag-24: 2.2.0][conan contracts] · :octicons-milestone-24: `none`/`gsl-lite`/`ms-gsl` (Default: `gsl-lite`) + + Enables checking of preconditions and additional asserts in the code. + + [conan contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 ### Conan configuration properties @@ -345,6 +352,14 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 +[`MP_UNITS_API_CONTRACTS`](#MP_UNITS_API_CONTRACTS){ #MP_UNITS_API_CONTRACTS } + +: [:octicons-tag-24: 2.2.0][cmake contracts] · :octicons-milestone-24: `NONE`/`GSL-LITE`/`MS-GSL` (Default: `GSL-LITE`) + + Enables checking of preconditions and additional asserts in the code. + + [cmake contracts]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 + #### Options for mp-units project developers [`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 36364f12..5325cd8d 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -23,21 +23,37 @@ cmake_minimum_required(VERSION 3.5) # find dependencies -if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) -endif() - if(${projectPrefix}BUILD_CXX_MODULES) add_library(example_utils INTERFACE) target_compile_features(example_utils INTERFACE cxx_std_20) target_compile_definitions(example_utils INTERFACE ${projectPrefix}MODULES) target_include_directories(example_utils INTERFACE include) - target_link_libraries(example_utils INTERFACE gsl::gsl-lite) endif() add_library(example_utils-headers INTERFACE) target_include_directories(example_utils-headers INTERFACE include) -target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite) + +if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=0) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") + if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite REQUIRED) + endif() + if(${projectPrefix}BUILD_CXX_MODULES) + target_link_libraries(example_utils INTERFACE gsl::gsl-lite) + endif() + target_link_libraries(example_utils-headers INTERFACE gsl::gsl-lite) + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=2) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") + if(NOT TARGET Microsoft.GSL::GSL) + find_package(Microsoft.GSL REQUIRED) + endif() + if(${projectPrefix}BUILD_CXX_MODULES) + target_link_libraries(example_utils INTERFACE Microsoft.GSL::GSL) + endif() + target_link_libraries(example_utils-headers INTERFACE Microsoft.GSL::GSL) + target_compile_definitions(mp-units-core INTERFACE ${projectPrefix}API_CONTRACTS=3) +endif() # # add_example(target ...) diff --git a/example/include/validated_type.h b/example/include/validated_type.h index 8b05aa8f..9a429e26 100644 --- a/example/include/validated_type.h +++ b/example/include/validated_type.h @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include // IWYU pragma: export @@ -50,13 +49,13 @@ public: requires std::copyable : value_(value) { - gsl_Expects(validate(value_)); + MP_UNITS_EXPECTS(validate(value_)); } constexpr explicit validated_type(T&& value) noexcept(std::is_nothrow_move_constructible_v) : value_(std::move(value)) { - gsl_Expects(validate(value_)); + MP_UNITS_EXPECTS(validate(value_)); } constexpr validated_type(const T& value, validated_tag) noexcept(std::is_nothrow_copy_constructible_v) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 363634aa..d33178c5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -68,6 +68,9 @@ cache_var_values(API_STRING_VIEW_RET AUTO TRUE FALSE) set(${projectPrefix}API_NO_CRTP AUTO CACHE STRING "Enable class definitions without CRTP idiom") cache_var_values(API_NO_CRTP AUTO TRUE FALSE) +set(${projectPrefix}API_CONTRACTS GSL-LITE CACHE STRING "Enable contract checking") +cache_var_values(API_CONTRACTS NONE GSL-LITE MS-GSL) + # C++ features check_cxx_feature_supported(__cpp_lib_format ${projectPrefix}LIB_FORMAT_SUPPORTED) check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) diff --git a/src/cmake/AddMPUnitsModule.cmake b/src/cmake/AddMPUnitsModule.cmake index be007c90..87457db2 100644 --- a/src/cmake/AddMPUnitsModule.cmake +++ b/src/cmake/AddMPUnitsModule.cmake @@ -54,7 +54,7 @@ function(add_mp_units_module name target_name) # validate and process arguments validate_unparsed(${name} ARG) - validate_arguments_exists(${name} ARG DEPENDENCIES MODULE_INTERFACE_UNIT) + validate_arguments_exists(${name} ARG MODULE_INTERFACE_UNIT) if(${projectPrefix}TARGET_SCOPE STREQUAL INTERFACE) set(SCOPE "INTERFACE") diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ed4525f3..8c6aada0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -31,15 +31,9 @@ function(set_feature_flag name) endif() endfunction() -# find dependencies -if(NOT TARGET gsl::gsl-lite) - find_package(gsl-lite REQUIRED) -endif() - # core library definition add_mp_units_module( core mp-units-core - DEPENDENCIES gsl::gsl-lite HEADERS include/mp-units/bits/core_gmf.h include/mp-units/bits/fmt.h include/mp-units/bits/get_associated_quantity.h @@ -91,6 +85,7 @@ set_feature_flag(API_STD_FORMAT) set_feature_flag(API_STRING_VIEW_RET) set_feature_flag(API_NO_CRTP) +# Text formatting if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO" AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED) ) @@ -100,6 +95,24 @@ if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_F target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} fmt::fmt) endif() +# Contracts checking +if(${projectPrefix}API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=0) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "GSL-LITE") + if(NOT TARGET gsl::gsl-lite) + find_package(gsl-lite REQUIRED) + endif() + target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} gsl::gsl-lite) + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=2) +elseif(${projectPrefix}API_CONTRACTS STREQUAL "MS-GSL") + if(NOT TARGET Microsoft.GSL::GSL) + find_package(Microsoft.GSL REQUIRED) + endif() + target_link_libraries(mp-units-core ${${projectPrefix}TARGET_SCOPE} Microsoft.GSL::GSL) + target_compile_definitions(mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}API_CONTRACTS=3) +endif() + +# C++20 modules if(${projectPrefix}BUILD_CXX_MODULES) if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18) @@ -112,6 +125,7 @@ if(${projectPrefix}BUILD_CXX_MODULES) endif() endif() +# UTF-8 source and execution character set if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") target_compile_options( mp-units-core ${${projectPrefix}TARGET_SCOPE} diff --git a/src/core/include/mp-units/bits/core_gmf.h b/src/core/include/mp-units/bits/core_gmf.h index a64e46b1..f7e4b9ba 100644 --- a/src/core/include/mp-units/bits/core_gmf.h +++ b/src/core/include/mp-units/bits/core_gmf.h @@ -22,7 +22,6 @@ #pragma once -#include #include #include #include diff --git a/src/core/include/mp-units/bits/fmt.h b/src/core/include/mp-units/bits/fmt.h index c4848d67..8e84ac0b 100644 --- a/src/core/include/mp-units/bits/fmt.h +++ b/src/core/include/mp-units/bits/fmt.h @@ -35,7 +35,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -138,7 +137,7 @@ template template [[nodiscard]] constexpr std::make_unsigned_t to_unsigned(Int value) { - gsl_Expects(std::is_unsigned_v || value >= 0); + MP_UNITS_EXPECTS(std::is_unsigned_v || value >= 0); return static_cast>(value); } @@ -201,7 +200,7 @@ MP_UNITS_EXPORT_END template [[nodiscard]] constexpr int parse_nonnegative_int(const Char*& begin, const Char* end, int error_value) { - gsl_Expects(begin != end && '0' <= *begin && *begin <= '9'); + MP_UNITS_EXPECTS(begin != end && '0' <= *begin && *begin <= '9'); unsigned value = 0, prev = 0; auto p = begin; do { @@ -262,7 +261,7 @@ template template [[nodiscard]] constexpr const Char* parse_arg_id(const Char* begin, const Char* end, Handler& handler) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); Char c = *begin; if (c != '}' && c != ':') return ::mp_units::detail::do_parse_arg_id(begin, end, handler); handler.on_auto(); @@ -304,7 +303,7 @@ template fmt_arg_ref& ref, MP_UNITS_STD_FMT::basic_format_parse_context& ctx) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); if ('0' <= *begin && *begin <= '9') { const int val = ::mp_units::detail::parse_nonnegative_int(begin, end, -1); if (val != -1) @@ -341,7 +340,7 @@ template [[nodiscard]] constexpr const Char* parse_align(const Char* begin, const Char* end, Specs& specs, fmt_align default_align = fmt_align::none) { - gsl_Expects(begin != end); + MP_UNITS_EXPECTS(begin != end); auto align = fmt_align::none; auto p = begin + code_point_length(begin); if (end - p <= 0) p = begin; diff --git a/src/core/include/mp-units/bits/ratio.h b/src/core/include/mp-units/bits/ratio.h index 96c67747..bcf778dd 100644 --- a/src/core/include/mp-units/bits/ratio.h +++ b/src/core/include/mp-units/bits/ratio.h @@ -24,9 +24,9 @@ #include #include +#include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -44,16 +44,16 @@ template { constexpr std::intmax_t c = std::uintmax_t{1} << (sizeof(std::intmax_t) * 4); - const std::intmax_t a0 = abs(lhs) % c; - const std::intmax_t a1 = abs(lhs) / c; - const std::intmax_t b0 = abs(rhs) % c; - const std::intmax_t b1 = abs(rhs) / c; + [[maybe_unused]] const std::intmax_t a0 = abs(lhs) % c; + [[maybe_unused]] const std::intmax_t a1 = abs(lhs) / c; + [[maybe_unused]] const std::intmax_t b0 = abs(rhs) % c; + [[maybe_unused]] const std::intmax_t b1 = abs(rhs) / c; // overflow in multiplication - gsl_Assert(a1 == 0 || b1 == 0); - gsl_Assert(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise) - gsl_Assert(b0 * a0 <= INTMAX_MAX); - gsl_Assert((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); + MP_UNITS_ASSERT(a1 == 0 || b1 == 0); + MP_UNITS_ASSERT(a0 * b1 + b0 * a1 < (c >> 1)); // NOLINT(hicpp-signed-bitwise) + MP_UNITS_ASSERT(b0 * a0 <= INTMAX_MAX); + MP_UNITS_ASSERT((a0 * b1 + b0 * a1) * c <= INTMAX_MAX - b0 * a0); return lhs * rhs; } @@ -72,7 +72,7 @@ MP_UNITS_EXPORT struct ratio { // NOLINTNEXTLINE(bugprone-easily-swappable-parameters, google-explicit-constructor, hicpp-explicit-conversions) MP_UNITS_CONSTEVAL explicit(false) ratio(std::intmax_t n, std::intmax_t d = 1) : num{n}, den{d} { - gsl_Expects(den != 0); + MP_UNITS_EXPECTS(den != 0); if (num == 0) den = 1; else { @@ -112,9 +112,9 @@ MP_UNITS_EXPORT struct ratio { if (r1.num == r2.num && r1.den == r2.den) return ratio{r1.num, r1.den}; // gcd(a/b,c/d) = gcd(a⋅d, c⋅b) / b⋅d - gsl_Assert(std::numeric_limits::max() / r1.num > r2.den); - gsl_Assert(std::numeric_limits::max() / r2.num > r1.den); - gsl_Assert(std::numeric_limits::max() / r1.den > r2.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r1.num > r2.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r2.num > r1.den); + MP_UNITS_ASSERT(std::numeric_limits::max() / r1.den > r2.den); const std::intmax_t num = std::gcd(r1.num * r2.den, r2.num * r1.den); const std::intmax_t den = r1.den * r2.den; diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 227551c7..a9c21813 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -102,5 +102,37 @@ MP_UNITS_DIAGNOSTIC_POP #endif // IWYU pragma: end_exports +#endif + +#if MP_UNITS_API_CONTRACTS == 2 || __has_include() + +#include + +#define MP_UNITS_EXPECTS(expr) gsl_Expects(expr) +#define MP_UNITS_EXPECTS_DEBUG(expr) gsl_ExpectsDebug(expr) +#define MP_UNITS_ASSERT(expr) gsl_Assert(expr) +#define MP_UNITS_ASSERT_DEBUG(expr) gsl_AssertDebug(expr) + +#elif MP_UNITS_API_CONTRACTS == 3 || __has_include() + +#include +#include + +#define MP_UNITS_EXPECTS(expr) Expects(expr) +#if defined NDEBUG +#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast(0) +#else +#define MP_UNITS_EXPECTS_DEBUG(expr) Expects(expr) +#endif +#define MP_UNITS_ASSERT(expr) Expects(expr) +#define MP_UNITS_ASSERT_DEBUG(expr) assert(expr) + +#else + +#define MP_UNITS_EXPECTS(expr) static_cast(0) +#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast(0) +#define MP_UNITS_ASSERT(expr) static_cast(0) +#define MP_UNITS_ASSERT_DEBUG(expr) static_cast(0) + #endif // NOLINTEND(cppcoreguidelines-macro-usage) diff --git a/src/core/include/mp-units/ext/fixed_string.h b/src/core/include/mp-units/ext/fixed_string.h index 1b1d13cf..c67332bd 100644 --- a/src/core/include/mp-units/ext/fixed_string.h +++ b/src/core/include/mp-units/ext/fixed_string.h @@ -32,7 +32,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -78,7 +77,7 @@ public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) consteval explicit(false) basic_fixed_string(const CharT (&txt)[N + 1]) noexcept { - gsl_Expects(txt[N] == CharT{}); + MP_UNITS_EXPECTS(txt[N] == CharT{}); for (std::size_t i = 0; i < N; ++i) data_[i] = txt[i]; } @@ -86,7 +85,7 @@ public: requires std::convertible_to, CharT> constexpr basic_fixed_string(It begin, S end) { - gsl_Expects(std::distance(begin, end) == N); + MP_UNITS_EXPECTS(std::distance(begin, end) == N); for (auto it = data_; begin != end; ++begin, ++it) *it = *begin; } @@ -94,7 +93,7 @@ public: requires std::convertible_to, CharT> constexpr basic_fixed_string(std::from_range_t, R&& r) { - gsl_Expects(std::ranges::size(r) == N); + MP_UNITS_EXPECTS(std::ranges::size(r) == N); for (auto it = data_; auto&& v : std::forward(r)) *it++ = std::forward(v); } @@ -120,7 +119,7 @@ public: // element access [[nodiscard]] constexpr const_reference operator[](size_type pos) const { - gsl_Expects(pos < N); + MP_UNITS_EXPECTS(pos < N); return data()[pos]; } @@ -131,12 +130,12 @@ public: } [[nodiscard]] constexpr const_reference front() const { - gsl_Expects(!empty()); + MP_UNITS_EXPECTS(!empty()); return (*this)[0]; } [[nodiscard]] constexpr const_reference back() const { - gsl_Expects(!empty()); + MP_UNITS_EXPECTS(!empty()); return (*this)[N - 1]; } @@ -191,7 +190,7 @@ public: [[nodiscard]] consteval friend basic_fixed_string operator+( const basic_fixed_string& lhs, const CharT (&rhs)[N2]) noexcept { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); CharT txt[N + N2]; CharT* it = txt; for (CharT c : lhs) *it++ = c; @@ -203,7 +202,7 @@ public: [[nodiscard]] consteval friend basic_fixed_string operator+( const CharT (&lhs)[N1], const basic_fixed_string& rhs) noexcept { - gsl_Expects(lhs[N1 - 1] == CharT{}); + MP_UNITS_EXPECTS(lhs[N1 - 1] == CharT{}); CharT txt[N1 + N]; CharT* it = txt; for (size_t i = 0; i != N1 - 1; ++i) *it++ = lhs[i]; @@ -222,7 +221,7 @@ public: template [[nodiscard]] friend consteval bool operator==(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); return lhs.view() == std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } @@ -235,7 +234,7 @@ public: template [[nodiscard]] friend consteval auto operator<=>(const basic_fixed_string& lhs, const CharT (&rhs)[N2]) { - gsl_Expects(rhs[N2 - 1] == CharT{}); + MP_UNITS_EXPECTS(rhs[N2 - 1] == CharT{}); return lhs.view() <=> std::basic_string_view(std::cbegin(rhs), std::cend(rhs) - 1); } diff --git a/src/core/include/mp-units/framework/dimension.h b/src/core/include/mp-units/framework/dimension.h index a708afc2..efa0810a 100644 --- a/src/core/include/mp-units/framework/dimension.h +++ b/src/core/include/mp-units/framework/dimension.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -280,7 +280,8 @@ template Out, typename... Expr> constexpr Out dimension_symbol_impl(Out out, const derived_dimension&, const dimension_symbol_formatting& fmt, bool negative_power) { - gsl_Expects(negative_power == false); + (void)negative_power; + MP_UNITS_EXPECTS(negative_power == false); return dimension_symbol_impl(out, typename derived_dimension::_num_{}, typename derived_dimension::_den_{}, fmt); } diff --git a/src/core/include/mp-units/framework/quantity.h b/src/core/include/mp-units/framework/quantity.h index a4880f48..f67a79bd 100644 --- a/src/core/include/mp-units/framework/quantity.h +++ b/src/core/include/mp-units/framework/quantity.h @@ -26,6 +26,7 @@ // IWYU pragma: private, include #include #include +#include #include #include #include @@ -36,7 +37,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #endif @@ -353,7 +353,7 @@ public: friend constexpr decltype(auto) operator%=(Q&& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != zero()); lhs.numerical_value_is_an_implementation_detail_ %= rhs.numerical_value_is_an_implementation_detail_; return std::forward(lhs); } @@ -393,7 +393,7 @@ public: } friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v) { - gsl_ExpectsDebug(v != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); lhs.numerical_value_is_an_implementation_detail_ /= v; return std::forward(lhs); } @@ -407,7 +407,7 @@ public: } friend constexpr decltype(auto) operator/=(Q1&& lhs, const Q2& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); lhs.numerical_value_is_an_implementation_detail_ /= rhs.numerical_value_is_an_implementation_detail_; return std::forward(lhs); } @@ -451,7 +451,7 @@ template detail::CommonlyInvocableQuantities, quantity, quantity> [[nodiscard]] constexpr Quantity auto operator%(const quantity& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); using ret = detail::common_quantity_for, quantity, quantity>; const ret ret_lhs(lhs); const ret ret_rhs(rhs); @@ -486,7 +486,7 @@ template requires detail::InvocableQuantities, quantity, quantity> [[nodiscard]] constexpr Quantity auto operator/(const quantity& lhs, const quantity& rhs) { - gsl_ExpectsDebug(rhs != rhs.zero()); + MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero()); return quantity{lhs.numerical_value_ref_in(get_unit(R1)) / rhs.numerical_value_ref_in(get_unit(R2)), R1 / R2}; } @@ -495,7 +495,7 @@ template detail::InvokeResultOf, Rep, const Value&> [[nodiscard]] constexpr QuantityOf auto operator/(const quantity& q, const Value& v) { - gsl_ExpectsDebug(v != quantity_values::zero()); + MP_UNITS_EXPECTS_DEBUG(v != quantity_values::zero()); return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R}; } diff --git a/src/core/include/mp-units/framework/symbol_text.h b/src/core/include/mp-units/framework/symbol_text.h index f7c3efaa..40f93a9a 100644 --- a/src/core/include/mp-units/framework/symbol_text.h +++ b/src/core/include/mp-units/framework/symbol_text.h @@ -27,11 +27,11 @@ // TODO use when moved to C++20 modules (parsing takes too long for each translation unit) #include #include +#include #include #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include // IWYU pragma: export #include #include @@ -95,34 +95,34 @@ public: // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) symbol_text(char ch) : unicode_(static_cast(ch)), ascii_(ch) { - gsl_Expects(detail::is_basic_literal_character_set_char(ch)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set_char(ch)); } // NOLINTNEXTLINE(*-avoid-c-arrays, google-explicit-constructor, hicpp-explicit-conversions) consteval explicit(false) symbol_text(const char (&txt)[N + 1]) : unicode_(detail::to_u8string(basic_fixed_string{txt})), ascii_(txt) { - gsl_Expects(txt[N] == char{}); - gsl_Expects(detail::is_basic_literal_character_set(txt)); + MP_UNITS_EXPECTS(txt[N] == char{}); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt)); } // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions) constexpr explicit(false) symbol_text(const fixed_string& txt) : unicode_(detail::to_u8string(txt)), ascii_(txt) { - gsl_Expects(detail::is_basic_literal_character_set(txt.data_)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(txt.data_)); } // NOLINTNEXTLINE(*-avoid-c-arrays) consteval symbol_text(const char8_t (&u)[N + 1], const char (&a)[M + 1]) : unicode_(u), ascii_(a) { - gsl_Expects(u[N] == char8_t{}); - gsl_Expects(a[M] == char{}); - gsl_Expects(detail::is_basic_literal_character_set(a)); + MP_UNITS_EXPECTS(u[N] == char8_t{}); + MP_UNITS_EXPECTS(a[M] == char{}); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a)); } constexpr symbol_text(const fixed_u8string& u, const fixed_string& a) : unicode_(u), ascii_(a) { - gsl_Expects(detail::is_basic_literal_character_set(a.data_)); + MP_UNITS_EXPECTS(detail::is_basic_literal_character_set(a.data_)); } [[nodiscard]] constexpr const auto& unicode() const { return unicode_; } @@ -130,7 +130,7 @@ public: [[nodiscard]] constexpr bool empty() const { - gsl_AssertDebug(unicode().empty() == ascii().empty()); + MP_UNITS_ASSERT_DEBUG(unicode().empty() == ascii().empty()); return unicode().empty(); } diff --git a/src/core/include/mp-units/framework/unit.h b/src/core/include/mp-units/framework/unit.h index 8e8ad5df..bc9cdf58 100644 --- a/src/core/include/mp-units/framework/unit.h +++ b/src/core/include/mp-units/framework/unit.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,6 @@ #include #ifndef MP_UNITS_IN_MODULE_INTERFACE -#include #include #include #include @@ -808,7 +808,8 @@ template Out, typename... Expr> constexpr Out unit_symbol_impl(Out out, const derived_unit&, const unit_symbol_formatting& fmt, bool negative_power) { - gsl_Expects(negative_power == false); + (void)negative_power; + MP_UNITS_EXPECTS(negative_power == false); return unit_symbol_impl(out, typename derived_unit::_num_{}, typename derived_unit::_den_{}, fmt); } diff --git a/src/mp-unitsConfig.cmake b/src/mp-unitsConfig.cmake index b42e3b55..017d6131 100644 --- a/src/mp-unitsConfig.cmake +++ b/src/mp-unitsConfig.cmake @@ -26,6 +26,10 @@ if(NOT MP_UNITS_API_STD_FORMAT) find_dependency(fmt) endif() -find_dependency(gsl-lite) +if(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE") + find_dependency(gsl-lite) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL") + find_dependency(Microsoft.GSL) +endif() include("${CMAKE_CURRENT_LIST_DIR}/mp-unitsTargets.cmake") diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index 074aa5fd..12ef88bd 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -28,3 +28,11 @@ find_package(mp-units REQUIRED) add_executable(test_package test_package.cpp) target_link_libraries(test_package PRIVATE mp-units::mp-units) target_compile_definitions(test_package PRIVATE MP_UNITS_API_STD_FORMAT=$) + +if(MP_UNITS_API_CONTRACTS STREQUAL "NONE") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=0) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "GSL-LITE") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=2) +elseif(MP_UNITS_API_CONTRACTS STREQUAL "MS-GSL") + target_compile_definitions(test_package PRIVATE MP_UNITS_API_CONTRACTS=3) +endif()