feat: allow configuring GSL library use

Resolves #576
This commit is contained in:
Mateusz Pusz
2024-05-30 12:23:14 +02:00
parent 3a792b4057
commit a6562acde5
20 changed files with 199 additions and 94 deletions

View File

@ -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

View File

@ -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

View File

@ -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":

View File

@ -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 }

View File

@ -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 <depependencies>...)

View File

@ -22,7 +22,6 @@
#pragma once
#include <gsl/gsl-lite.hpp>
#include <mp-units/bits/hacks.h>
#include <mp-units/compat_macros.h>
#include <compare> // IWYU pragma: export
@ -50,13 +49,13 @@ public:
requires std::copyable<T>
: value_(value)
{
gsl_Expects(validate(value_));
MP_UNITS_EXPECTS(validate(value_));
}
constexpr explicit validated_type(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>) :
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<T>)

View File

@ -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)

View File

@ -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")

View File

@ -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}

View File

@ -22,7 +22,6 @@
#pragma once
#include <gsl/gsl-lite.hpp>
#include <mp-units/bits/hacks.h>
#include <mp-units/compat_macros.h>
#include <array>

View File

@ -35,7 +35,6 @@
#include <mp-units/ext/algorithm.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <concepts>
#include <cstdint>
@ -138,7 +137,7 @@ template<typename Char>
template<typename Int>
[[nodiscard]] constexpr std::make_unsigned_t<Int> to_unsigned(Int value)
{
gsl_Expects(std::is_unsigned_v<Int> || value >= 0);
MP_UNITS_EXPECTS(std::is_unsigned_v<Int> || value >= 0);
return static_cast<std::make_unsigned_t<Int>>(value);
}
@ -201,7 +200,7 @@ MP_UNITS_EXPORT_END
template<typename Char>
[[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<typename Char, typename Handler>
template<typename Char, typename Handler>
[[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<typename Char>
fmt_arg_ref<Char>& ref,
MP_UNITS_STD_FMT::basic_format_parse_context<Char>& 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<typename Char, typename Specs>
[[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;

View File

@ -24,9 +24,9 @@
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstdint>
#include <numeric>
@ -44,16 +44,16 @@ template<typename T>
{
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<std::intmax_t>::max() / r1.num > r2.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r2.num > r1.den);
gsl_Assert(std::numeric_limits<std::intmax_t>::max() / r1.den > r2.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::max() / r1.num > r2.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::max() / r2.num > r1.den);
MP_UNITS_ASSERT(std::numeric_limits<std::intmax_t>::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;

View File

@ -102,5 +102,37 @@ MP_UNITS_DIAGNOSTIC_POP
#endif
// IWYU pragma: end_exports
#endif
#if MP_UNITS_API_CONTRACTS == 2 || __has_include(<gsl/gsl-lite.hpp>)
#include <gsl/gsl-lite.hpp>
#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(<gsl/gsl>)
#include <gsl/gsl>
#include <cassert>
#define MP_UNITS_EXPECTS(expr) Expects(expr)
#if defined NDEBUG
#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast<void>(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<void>(0)
#define MP_UNITS_EXPECTS_DEBUG(expr) static_cast<void>(0)
#define MP_UNITS_ASSERT(expr) static_cast<void>(0)
#define MP_UNITS_ASSERT_DEBUG(expr) static_cast<void>(0)
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)

View File

@ -32,7 +32,6 @@
#include <mp-units/ext/type_traits.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstddef>
#include <cstdlib>
@ -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<std::iter_value_t<It>, 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<std::ranges::range_reference_t<R>, 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>(r)) *it++ = std::forward<decltype(v)>(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<CharT, N + N2 - 1, Traits> 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<CharT, N1 + N - 1, Traits> 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<size_t N2>
[[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<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
}
@ -235,7 +234,7 @@ public:
template<size_t N2>
[[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<CharT, Traits>(std::cbegin(rhs), std::cend(rhs) - 1);
}

View File

@ -26,6 +26,7 @@
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_traits.h>
#include <mp-units/framework/dimension_concepts.h>
@ -33,7 +34,6 @@
#include <mp-units/framework/symbol_text.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <cstdint>
#include <iterator>
@ -280,7 +280,8 @@ template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out dimension_symbol_impl(Out out, const derived_dimension<Expr...>&, 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<CharT>(out, typename derived_dimension<Expr...>::_num_{},
typename derived_dimension<Expr...>::_den_{}, fmt);
}

View File

@ -26,6 +26,7 @@
// IWYU pragma: private, include <mp-units/framework.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/sudo_cast.h>
#include <mp-units/compat_macros.h>
#include <mp-units/framework/customization_points.h>
#include <mp-units/framework/dimension_concepts.h>
#include <mp-units/framework/quantity_concepts.h>
@ -36,7 +37,6 @@
#include <mp-units/framework/unit_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <utility>
#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<Q>(lhs);
}
@ -393,7 +393,7 @@ public:
}
friend constexpr decltype(auto) operator/=(Q&& lhs, const Value& v)
{
gsl_ExpectsDebug(v != quantity_values<Value>::zero());
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
lhs.numerical_value_is_an_implementation_detail_ /= v;
return std::forward<Q>(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<Q1>(lhs);
}
@ -451,7 +451,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
detail::CommonlyInvocableQuantities<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator%(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& rhs)
{
gsl_ExpectsDebug(rhs != rhs.zero());
MP_UNITS_EXPECTS_DEBUG(rhs != rhs.zero());
using ret = detail::common_quantity_for<std::modulus<>, quantity<R1, Rep1>, quantity<R2, Rep2>>;
const ret ret_lhs(lhs);
const ret ret_rhs(rhs);
@ -486,7 +486,7 @@ template<auto R1, typename Rep1, auto R2, typename Rep2>
requires detail::InvocableQuantities<std::divides<>, quantity<R1, Rep1>, quantity<R2, Rep2>>
[[nodiscard]] constexpr Quantity auto operator/(const quantity<R1, Rep1>& lhs, const quantity<R2, Rep2>& 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<auto R, typename Rep, typename Value>
detail::InvokeResultOf<get_quantity_spec(R).character, std::divides<>, Rep, const Value&>
[[nodiscard]] constexpr QuantityOf<get_quantity_spec(R)> auto operator/(const quantity<R, Rep>& q, const Value& v)
{
gsl_ExpectsDebug(v != quantity_values<Value>::zero());
MP_UNITS_EXPECTS_DEBUG(v != quantity_values<Value>::zero());
return quantity{q.numerical_value_ref_in(get_unit(R)) / v, R};
}

View File

@ -27,11 +27,11 @@
// TODO use <algorithm> when moved to C++20 modules (parsing takes too long for each translation unit)
#include <mp-units/bits/hacks.h>
#include <mp-units/bits/module_macros.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/fixed_string.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <compare> // IWYU pragma: export
#include <cstddef>
#include <cstdint>
@ -95,34 +95,34 @@ public:
// NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
constexpr explicit(false) symbol_text(char ch) : unicode_(static_cast<char8_t>(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<N>& 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<N>& u, const fixed_string<M>& 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();
}

View File

@ -28,6 +28,7 @@
#include <mp-units/bits/module_macros.h>
#include <mp-units/bits/ratio.h>
#include <mp-units/bits/text_tools.h>
#include <mp-units/compat_macros.h>
#include <mp-units/ext/algorithm.h>
#include <mp-units/ext/fixed_string.h>
#include <mp-units/ext/type_name.h>
@ -40,7 +41,6 @@
#include <mp-units/framework/unit_concepts.h>
#ifndef MP_UNITS_IN_MODULE_INTERFACE
#include <gsl/gsl-lite.hpp>
#include <array>
#include <cstdint>
#include <iterator>
@ -808,7 +808,8 @@ template<typename CharT, std::output_iterator<CharT> Out, typename... Expr>
constexpr Out unit_symbol_impl(Out out, const derived_unit<Expr...>&, 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<CharT>(out, typename derived_unit<Expr...>::_num_{}, typename derived_unit<Expr...>::_den_{},
fmt);
}

View File

@ -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")

View File

@ -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=$<BOOL:${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()