feat: 💥 API-related Conan, CMake, and preprocessor options redesigned

This commit is contained in:
Mateusz Pusz
2024-04-16 21:48:36 +01:00
parent 7b57ce5ab1
commit 8e0a26b933
26 changed files with 490 additions and 207 deletions

View File

@ -221,13 +221,13 @@ jobs:
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile show -pr default conan profile show -pr default
- run: echo "use_fmtlib=$([ "${{ matrix.formatting }}" == "fmtlib" ] && echo "True" || echo "False")" >> $GITHUB_ENV - run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
- name: Create Conan package - name: Create Conan package
shell: bash shell: bash
run: | run: |
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \ conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \ -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 use_fmtlib=${{ env.use_fmtlib }} ${{ matrix.config.conan-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 }}
- name: Obtain package reference - name: Obtain package reference
id: get-package-ref id: get-package-ref
shell: bash shell: bash

View File

@ -215,12 +215,12 @@ jobs:
sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default sed -i.backup '/^\[settings\]$/,/^\[/ s/^compiler.cppstd=.*/compiler.cppstd=${{ matrix.std }}/' ~/.conan2/profiles/default
sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default sed -i.backup '/^\[settings\]$/,/^\[/ s/^build_type=.*/build_type=${{ matrix.build_type }}/' ~/.conan2/profiles/default
conan profile show -pr default conan profile show -pr default
- run: echo "use_fmtlib=$([ "${{ matrix.formatting }}" == "fmtlib" ] && echo "True" || echo "False")" >> $GITHUB_ENV - run: echo "std_format=$([ "${{ matrix.formatting }}" == "std::format" ] && echo "True" || echo "False")" >> $GITHUB_ENV
- name: Install Conan dependencies - name: Install Conan dependencies
shell: bash shell: bash
run: | run: |
conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.mp-units.build:all=False \ 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 use_fmtlib=${{ env.use_fmtlib }} -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }}
mv CMakeUserPresets.json src mv CMakeUserPresets.json src
- name: Configure mp-units CMake - name: Configure mp-units CMake
if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC'

View File

@ -105,7 +105,7 @@ jobs:
conan profile detect --force conan profile detect --force
conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss
mkdir _lgtm_build_dir && cd _lgtm_build_dir mkdir _lgtm_build_dir && cd _lgtm_build_dir
conan build .. -s compiler.cppstd=20 -c user.mp-units.build:all=True -o use_fmtlib=True -b missing conan build .. -s compiler.cppstd=20 -c user.mp-units.build:all=True -o std_format=False -b missing
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2 uses: github/codeql-action/analyze@v2

View File

@ -70,17 +70,17 @@ tasks:
gp sync-await python-init gp sync-await python-init
conan profile detect conan profile detect
conan config install $PWD/.gitpod/conan conan config install $PWD/.gitpod/conan
conan install . -pr gcc12 -o use_fmtlib=True -b missing conan install . -pr gcc12 -o std_format=False -b missing
conan install . -pr gcc12 -o use_fmtlib=True -b missing -s build_type=Debug conan install . -pr gcc12 -o std_format=False -b missing -s build_type=Debug
gp sync-done conan-gcc12-20 gp sync-done conan-gcc12-20
conan install . -pr gcc13 -b missing conan install . -pr gcc13 -o std_format=True -b missing
conan install . -pr gcc13 -b missing -s build_type=Debug conan install . -pr gcc13 -o std_format=True -b missing -s build_type=Debug
gp sync-done conan-gcc13-20 gp sync-done conan-gcc13-20
conan install . -pr clang16 -o use_fmtlib=True -b missing conan install . -pr clang16 -o std_format=False -b missing
conan install . -pr clang16 -o use_fmtlib=True -b missing -s build_type=Debug conan install . -pr clang16 -o std_format=False -b missing -s build_type=Debug
gp sync-done conan-clang16-20 gp sync-done conan-clang16-20
conan install . -pr clang17 -o cxx_modules=True -b missing conan install . -pr clang17 -o std_format=True -o cxx_modules=True -b missing
conan install . -pr clang17 -o cxx_modules=True -b missing -s build_type=Debug conan install . -pr clang17 -o std_format=True -o cxx_modules=True -b missing -s build_type=Debug
gp sync-done conan-clang17-20 gp sync-done conan-clang17-20
conan remote login -p $ARTIFACTORY_TOKEN conan-gitpod-mp-units $ARTIFACTORY_USER conan remote login -p $ARTIFACTORY_TOKEN conan-gitpod-mp-units $ARTIFACTORY_USER
conan upload "*" -r conan-gitpod-mp-units -c conan upload "*" -r conan-gitpod-mp-units -c
@ -88,29 +88,29 @@ tasks:
init: | init: |
gp sync-await conan-gcc12-20 gp sync-await conan-gcc12-20
source ${PYTHON_VENV}/bin/activate source ${PYTHON_VENV}/bin/activate
conan build . -pr gcc12 -o use_fmtlib=True conan build . -pr gcc12 -o std_format=False
conan build . -pr gcc12 -o use_fmtlib=True -s build_type=Debug conan build . -pr gcc12 -o std_format=False -s build_type=Debug
echo "🛠️ gcc-12 pre-build done for C++20, header files, and libfmt! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" echo "🛠️ gcc-12 pre-build done for C++20, header files, and libfmt! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: gcc-13-20 - name: gcc-13-20
init: | init: |
gp sync-await conan-gcc13-20 gp sync-await conan-gcc13-20
source ${PYTHON_VENV}/bin/activate source ${PYTHON_VENV}/bin/activate
conan build . -pr gcc13 conan build . -pr gcc13 -o std_format=True
conan build . -pr gcc13 -s build_type=Debug conan build . -pr gcc13 -o std_format=True -s build_type=Debug
echo "🛠️ gcc-13 pre-build done for C++20 and header files! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" echo "🛠️ gcc-13 pre-build done for C++20 and header files! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-16-20 - name: clang-16-20
init: | init: |
gp sync-await conan-clang16-20 gp sync-await conan-clang16-20
source ${PYTHON_VENV}/bin/activate source ${PYTHON_VENV}/bin/activate
conan build . -pr clang16 -o use_fmtlib=True conan build . -pr clang16 -o std_format=False
conan build . -pr clang16 -o use_fmtlib=True -s build_type=Debug conan build . -pr clang16 -o std_format=False -s build_type=Debug
echo "🛠️ clang-16 pre-build done for C++20, header files, and libfmt! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" echo "🛠️ clang-16 pre-build done for C++20, header files, and libfmt! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: clang-17-20 - name: clang-17-20
init: | init: |
gp sync-await conan-clang17-20 gp sync-await conan-clang17-20
source ${PYTHON_VENV}/bin/activate source ${PYTHON_VENV}/bin/activate
conan build . -pr clang17 -o cxx_modules=True conan build . -pr clang17 -o std_format=True -o cxx_modules=True
conan build . -pr clang17 -o cxx_modules=True -s build_type=Debug conan build . -pr clang17 -o std_format=True -o cxx_modules=True -s build_type=Debug
echo "🛠️ clang-17 pre-build done for C++20! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️" echo "🛠️ clang-17 pre-build done for C++20! You can close this terminal and use 'Build' button in the VSCode status bar for incremental builds. 🛠️"
- name: documentation - name: documentation
init: | init: |

View File

@ -77,7 +77,7 @@ Before submission, please remember to check if the code compiles fine on the sup
The CI will check it anyway but it is good to check at least some of the configurations before pushing changes. The CI will check it anyway but it is good to check at least some of the configurations before pushing changes.
Especially older compilers can be tricky as those do not support all the C++20 features well enough. The official Especially older compilers can be tricky as those do not support all the C++20 features well enough. The official
list of supported compilers can be always found in the list of supported compilers can be always found in the
[Installation And Usage](https://mpusz.github.io/mp-units/latest/getting_started/installation_and_usage/#cpp-compiler-support) [Installation And Usage](https://mpusz.github.io/mp-units/latest/getting_started/cpp_compiler_support)
chapter of our documentation. chapter of our documentation.

View File

@ -25,13 +25,20 @@ import re
from conan import ConanFile from conan import ConanFile
from conan.errors import ConanInvalidConfiguration from conan.errors import ConanInvalidConfiguration
from conan.tools.build import can_run, check_min_cppstd from conan.tools.build import can_run, default_cppstd, valid_min_cppstd
from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout from conan.tools.cmake import CMake, CMakeDeps, CMakeToolchain, cmake_layout
from conan.tools.files import copy, load, rmdir from conan.tools.files import copy, load, rmdir
required_conan_version = ">=2.0.0" required_conan_version = ">=2.0.0"
def loose_lt_semver(v1, v2):
lv1 = [int(v) for v in v1.split(".")]
lv2 = [int(v) for v in v2.split(".")]
min_length = min(len(lv1), len(lv2))
return lv1[:min_length] < lv2[:min_length]
class MPUnitsConan(ConanFile): class MPUnitsConan(ConanFile):
name = "mp-units" name = "mp-units"
homepage = "https://github.com/mpusz/mp-units" homepage = "https://github.com/mpusz/mp-units"
@ -54,14 +61,18 @@ class MPUnitsConan(ConanFile):
url = "https://github.com/mpusz/mp-units" url = "https://github.com/mpusz/mp-units"
settings = "os", "arch", "compiler", "build_type" settings = "os", "arch", "compiler", "build_type"
options = { options = {
"cxx_modules": [True, False], "cxx_modules": ["auto", True, False],
"use_fmtlib": [True, False], "std_format": ["auto", True, False],
"string_view_ret": ["auto", True, False],
"no_crtp": ["auto", True, False],
} }
default_options = { default_options = {
"cxx_modules": False, "cxx_modules": "auto",
"use_fmtlib": False, "std_format": "auto",
"string_view_ret": "auto",
"no_crtp": "auto",
} }
tool_requires = "cmake/[>=3.28.1]" tool_requires = "cmake/[>=3.29]"
exports = "LICENSE.md" exports = "LICENSE.md"
exports_sources = ( exports_sources = (
"docs/*", "docs/*",
@ -75,27 +86,106 @@ class MPUnitsConan(ConanFile):
no_copy_source = True no_copy_source = True
@property @property
def _minimum_compilers_version(self): def _feature_compatibility(self):
return { return {
"gcc": "12", "minimum_support": {
"clang": "16", "std": "20",
"apple-clang": "15", "compiler": {
# , "msvc": "192" "gcc": "12",
"clang": "16",
"apple-clang": "15",
"msvc": "",
},
},
"std_format": {
"std": "20",
"compiler": {
"gcc": "13",
"clang": "17",
"apple-clang": "",
"msvc": "",
},
},
"cxx_modules": {
"std": "20",
"compiler": {"gcc": "14", "clang": "17", "apple-clang": "", "msvc": ""},
},
"static_constexpr_vars_in_constexpr_func": {
"std": "23",
"compiler": {"gcc": "13", "clang": "17", "apple-clang": "", "msvc": ""},
},
"explicit_this": {
"std": "23",
"compiler": {
"gcc": "14",
"clang": "18",
"apple-clang": "",
"msvc": "",
},
},
} }
@property @property
def _std_format_minimum_compilers_version(self): def _option_feature_map(self):
return { return {
"gcc": "13", "std_format": "std_format",
"clang": "17", "cxx_modules": "cxx_modules",
# , "apple-clang": "15" "string_view_ret": "static_constexpr_vars_in_constexpr_func",
# , "msvc": "192" "no_crtp": "explicit_this",
} }
def _check_feature_supported(self, name, feature_name=name):
compiler = self.settings.compiler
cppstd = compiler.get_safe("cppstd", default_cppstd(self))
feature = self._feature_compatibility[feature_name]
# check C++ version
if not valid_min_cppstd(self, feature["std"]):
raise ConanInvalidConfiguration(
f"'{name}' requires at least cppstd={feature['std']} ({cppstd} in use)",
)
# check compiler version
min_version = feature["compiler"].get(str(compiler))
if min_version is None:
# not tested compiler being used - use at your own risk
return
if min_version == "":
raise ConanInvalidConfiguration(
f"'{name}' is not yet supported by any known {compiler} compiler"
)
if loose_lt_semver(str(compiler.version), min_version):
raise ConanInvalidConfiguration(
f"'{name}' requires at least {compiler}-{min_version} ({compiler}-{compiler.version} in use)"
)
def _is_feature_enabled(self, name):
compiler = self.settings.compiler
opt = self.options.get_safe(name)
feature_name = self._option_feature_map[name]
feature = self._feature_compatibility[feature_name]
min_version = feature["compiler"].get(str(compiler))
return bool(
opt is True
or (
opt == "auto"
and min_version
and not loose_lt_semver(str(compiler.version), min_version)
)
)
@property @property
def _build_all(self): def _build_all(self):
return bool(self.conf.get("user.mp-units.build:all", default=False)) return bool(self.conf.get("user.mp-units.build:all", default=False))
@property
def _build_cxx_modules(self):
return self._is_feature_enabled("cxx_modules")
@property
def _use_fmtlib(self):
return not self._is_feature_enabled("std_format")
@property @property
def _skip_la(self): def _skip_la(self):
return bool(self.conf.get("user.mp-units.build:skip_la", default=False)) return bool(self.conf.get("user.mp-units.build:skip_la", default=False))
@ -109,7 +199,7 @@ class MPUnitsConan(ConanFile):
def requirements(self): def requirements(self):
self.requires("gsl-lite/0.41.0") self.requires("gsl-lite/0.41.0")
if self.options.use_fmtlib: if self._use_fmtlib:
self.requires("fmt/10.2.1") self.requires("fmt/10.2.1")
def build_requirements(self): def build_requirements(self):
@ -119,27 +209,10 @@ class MPUnitsConan(ConanFile):
self.test_requires("wg21-linear_algebra/0.7.3") self.test_requires("wg21-linear_algebra/0.7.3")
def validate(self): def validate(self):
check_min_cppstd(self, "20") self._check_feature_supported("mp-units", "minimum_support")
for key, value in self._option_feature_map.items():
def loose_lt_semver(v1, v2): if self.options.get_safe(key) is True:
lv1 = [int(v) for v in v1.split(".")] self._check_feature_supported(key, value)
lv2 = [int(v) for v in v2.split(".")]
min_length = min(len(lv1), len(lv2))
return lv1[:min_length] < lv2[:min_length]
compiler = self.settings.compiler
min_version = self._minimum_compilers_version.get(str(compiler))
if min_version and loose_lt_semver(str(compiler.version), min_version):
raise ConanInvalidConfiguration(
f"{self.ref} requires at least {compiler} {min_version} ({compiler.version} in use)"
)
if not self.options.use_fmtlib:
min_version = self._std_format_minimum_compilers_version.get(str(compiler))
if min_version and loose_lt_semver(str(compiler.version), min_version):
raise ConanInvalidConfiguration(
f"`std::format` requires at least {compiler} {min_version} ({compiler.version} in use). "
"Use `-o use_fmtlib=True` instead."
)
def layout(self): def layout(self):
cmake_layout(self) cmake_layout(self)
@ -148,13 +221,19 @@ class MPUnitsConan(ConanFile):
tc = CMakeToolchain(self) tc = CMakeToolchain(self)
if self._build_all: if self._build_all:
tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = True
if self.options.cxx_modules: tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la
if self._build_cxx_modules:
tc.cache_variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True tc.cache_variables["CMAKE_CXX_SCAN_FOR_MODULES"] = True
tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = True tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = str(
tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = ( self.options.cxx_modules
self._build_all and not self._skip_la ).upper()
) tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = str(
tc.cache_variables["MP_UNITS_USE_FMTLIB"] = bool(self.options.use_fmtlib) self.options.std_format
).upper()
tc.cache_variables["MP_UNITS_API_STRING_VIEW_RET"] = str(
self.options.string_view_ret
).upper()
tc.cache_variables["MP_UNITS_API_NO_CRTP"] = str(self.options.no_crtp).upper()
tc.generate() tc.generate()
deps = CMakeDeps(self) deps = CMakeDeps(self)
deps.generate() deps.generate()

View File

@ -0,0 +1,90 @@
# C++ compiler support (API/ABI) { #cpp-compiler-support }
!!! info
**mp-units** library tries to provide the best user experience possible with the C++ language.
To achieve that, it extensively uses the latest C++ language features.
Even though the library benefits from the latest C++ versions (if available), C++20 is enough
to compile and use all of the library's functionality. Newer features can be hidden behind
some [preprocessor macros](../users_guide/use_cases/wide_compatibility.md#compatibility-macros)
providing a backward-compatible way to use them.
The table below provides the minimum compiler version required to compile the code using a specific
C++ feature:
| C++ Feature | C++ version | gcc | clang | apple-clang | MSVC |
|-----------------------------------------------------------|:-----------:|:---:|:-----:|:-----------:|:----:|
| **Minimum support** | 20 | 12 | 16 | 15 | None |
| **`std::format`** | 20 | 13 | 17 | None | None |
| **C++ modules** | 20 | 14 | 17 | None | None |
| **Static `constexpr` variables in `constexpr` functions** | 23 | 13 | 17 | None | None |
| **Explicit `this` parameter** | 23 | 14 | 18 | None | None |
!!! important
Enabling/disabling features listed above may influence the API of the library and the ABI of
the customers' projects.
## `std::format`
- Provides [powerful text formatting capabilities](../users_guide/framework_basics/text_output.md#stdformat)
for C++.
- An alternative [fmtlib](https://github.com/fmtlib/fmt) library can be used instead if
- the C++ language feature is not supported,
- the customer's project did not switch to `std::format` yet (even when the compiler
supports it).
- To write code with wide compatibility
a [dedicated macro may be used](../users_guide/use_cases/wide_compatibility.md#mp_units_std_fmt).
- Tested with `__cpp_lib_format` [feature test macro](https://en.cppreference.com/w/cpp/feature_test).
- Related build options:
- Conan: [std_format](installation_and_usage.md#std_format)
- CMake: [MP_UNITS_API_STD_FORMAT](installation_and_usage.md#MP_UNITS_API_STD_FORMAT)
## C++ modules
- Provide new way to share declarations and definitions across translation units.
- If used, the library will distribute both "old-style" headers and module interface units
- associated with the same CMake targets.
- Even with full compiler support, a user may still decide to not pay for C++ modules compilation
if they are not needed by the customer's project.
- Feature test macro is not used for testing here because even if the compiler does not support
the entire C++ feature (e.g. header units), it is enough to build modules for this library.
- Related build options:
- Conan: [cxx_modules](installation_and_usage.md#cxx_modules)
- CMake: [MP_UNITS_BUILD_CXX_MODULES](installation_and_usage.md#MP_UNITS_BUILD_CXX_MODULES)
!!! note
More requirements for C++ modules support can be found in the
[CMake's documentation](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html).
## Static `constexpr` variables in `constexpr` functions
- Allows returning `std::string_view` from the
[`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol)
and [`dimension_symbol()`](../users_guide/framework_basics/text_output.md#dimension_symbol)
functions
- `std::string_view` type has a reference semantics so it has to point to a storage with
a longer lifetime.
- If this feature is not available, the API returns `mp_units::basic_fixed_string<CharT, N>` instead.
- Tested as `__cpp_constexpr >= 202211L` [feature test macro](https://en.cppreference.com/w/cpp/feature_test).
- Related build options:
- Conan: [string_view_ret](installation_and_usage.md#string_view_ret)
- CMake: [MP_UNITS_API_STRING_VIEW_RET](installation_and_usage.md#MP_UNITS_API_STRING_VIEW_RET)
## Explicit `this` parameter
- This feature removes the need for the usage of the CRTP idiom in the
[`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities).
- To write code with wide compatibility
a [dedicated macro may be used](../users_guide/use_cases/wide_compatibility.md#QUANTITY_SPEC).
- Tested with `__cpp_explicit_this_parameter` [feature test macro](https://en.cppreference.com/w/cpp/feature_test).
- Related build options:
- Conan: [no_crtp](installation_and_usage.md#no_crtp)
- CMake: [MP_UNITS_API_NO_CRTP](installation_and_usage.md#MP_UNITS_API_NO_CRTP)
*[CRTP]: Curiously Recurring Template Parameter

View File

@ -3,60 +3,12 @@
This chapter provides all the necessary information to obtain and build the code using **mp-units**. This chapter provides all the necessary information to obtain and build the code using **mp-units**.
It also describes how to build or distribute the library and generate its documentation. It also describes how to build or distribute the library and generate its documentation.
## C++ compiler support { #cpp-compiler-support }
!!! info
**mp-units** library tries to provide the best user experience possible with the C++ language.
To achieve that, it extensively uses C++20 features and the
[explicit object parameter](https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter)
from C++23.
Even though the library benefits from C++23 (if available), C++20 is enough to compile and
use all of the library's functionality. C++23 features are hidden behind
a [preprocessor macro](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities)
providing a backward-compatible way to use it.
The below table provides the minimum compiler version required to compile the code using the
specific feature:
| Feature | gcc | clang | apple-clang | MSVC |
|----------------------|:---:|:-----:|:-----------:|:----:|
| **Minimum support** | 12 | 16 | 15 | None |
| **`std::format`** | 13 | 17 | None | None |
| **C++ modules** | 14 | 17 | None | None |
| **C++23 extensions** | 14 | 18 | None | None |
More requirements for C++ modules support can be found in the
[CMake's documentation](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html).
## Modules
The **mp-units** library provides the following C++ modules.
```mermaid
flowchart TD
mp_units --- mp_units.systems --- mp_units.core
```
| C++ Module | CMake Target | Contents |
|--------------------|----------------------|----------------------------------------------------------|
| `mp_units.core` | `mp-units::core` | Core library framework and systems-independent utilities |
| `mp_units.systems` | `mp-units::systems` | All the systems of quantities and units |
| `mp_units` | `mp-units::mp-units` | Core + Systems |
!!! note
C++ modules are provided within the package only when [`cxx_modules`](#cxx_modules) Conan
option is set to `True`.
## Repository structure and dependencies ## Repository structure and dependencies
This repository contains three independent CMake-based projects: This repository contains three independent CMake-based projects:
- _./src_ - **_./src_**
- header-only project containing whole **mp-units** library - header-only project containing whole **mp-units** library
- _./src/CMakeList.txt_ file is intended as an **entry point for library users** - _./src/CMakeList.txt_ file is intended as an **entry point for library users**
@ -68,7 +20,7 @@ This repository contains three independent CMake-based projects:
- [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities - [{fmt}](https://github.com/fmtlib/fmt) to provide text formatting of quantities
(if `std::format` is not supported yet on a specific compiler). (if `std::format` is not supported yet on a specific compiler).
- _._ - **_._**
- project used as an **entry point for library development and CI/CD** - project used as an **entry point for library development and CI/CD**
- it wraps _./src_ project together with usage examples and tests - it wraps _./src_ project together with usage examples and tests
@ -79,7 +31,7 @@ This repository contains three independent CMake-based projects:
library based on proposal [P1385](https://wg21.link/P1385) used in some examples library based on proposal [P1385](https://wg21.link/P1385) used in some examples
and tests. and tests.
- *./test_package* - **_./test_package_**
- CMake library installation and Conan package verification. - CMake library installation and Conan package verification.
@ -104,6 +56,27 @@ This repository contains three independent CMake-based projects:
[FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-building-of-tests-and-examples). [FAQ](faq.md#why-dont-we-have-cmake-options-to-disable-building-of-tests-and-examples).
## Modules
The **mp-units** library provides the following C++ modules:
```mermaid
flowchart TD
mp_units --- mp_units.systems --- mp_units.core
```
| C++ Module | CMake Target | Contents |
|--------------------|----------------------|----------------------------------------------------------|
| `mp_units.core` | `mp-units::core` | Core library framework and systems-independent utilities |
| `mp_units.systems` | `mp-units::systems` | All the systems of quantities and units |
| `mp_units` | `mp-units::mp-units` | Core + Systems |
!!! note
C++ modules are provided within the package only when [`cxx_modules`](#cxx_modules) Conan
option is set to `True`.
## Obtaining dependencies ## Obtaining dependencies
This library assumes that most of the dependencies will be provided by the This library assumes that most of the dependencies will be provided by the
@ -118,7 +91,7 @@ The rest of the dependencies responsible for documentation generation are provid
In case you are not familiar with Conan, to install it (or upgrade) just do: In case you are not familiar with Conan, to install it (or upgrade) just do:
```shell ```shell
pip3 install -U conan pip install -U conan
``` ```
After that, you might need to add a custom profile file for your development environment After that, you might need to add a custom profile file for your development environment
@ -173,30 +146,59 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
## Build options ## Build options
!!! note
Most of the below options are related to the C++ language features available in the compilers.
Please refer to the [C++ compiler support](cpp_compiler_support.md) chapter to learn more
about which C++ features are required and which compiler support them.
### Conan options ### Conan options
[cxx_modules](#cxx_modules){ #cxx_modules } [cxx_modules](#cxx_modules){ #cxx_modules }
: [:octicons-tag-24: 2.2.0][cxx modules support] · :octicons-milestone-24: `True`/`False` (Default: `False`) : [:octicons-tag-24: 2.2.0][conan C++ modules support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
Configures CMake to add C++ modules to the list of default targets. Configures CMake to add C++ modules to the list of default targets.
[cxx modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [conan C++ modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[use_fmtlib](#use_fmtlib){ #use_fmtlib } [std_format](#std_format){ #std_format }
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `True`/`False` (Default: `False`) : [:octicons-tag-24: 2.2.0][conan std::format support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
Forces usage of [{fmt}](https://github.com/fmtlib/fmt) library instead of the C++20 Standard Enables the usage of [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format)
Library features. and associated facilities for text formatting. If it is not supported, then
the [{fmt}](https://github.com/fmtlib/fmt) library is used instead.
[conan std::format support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[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`)
Enables returning `std::string_view` from the
[`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol)
and [`dimension_symbol()`](../users_guide/framework_basics/text_output.md#dimension_symbol)
functions. If this feature is not available, those functions will return
`mp_units::basic_fixed_string<CharT, N>` instead.
[conan returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[no_crtp](#no_crtp){ #no_crtp }
: [:octicons-tag-24: 2.2.0][conan no crtp support] · :octicons-milestone-24: `auto`/`True`/`False` (Default: `auto`)
Removes the need for the usage of the CRTP idiom in the
[`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities).
[conan no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[use fmtlib support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### Conan configuration properties ### Conan configuration properties
[`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all } [`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all }
: [:octicons-tag-24: 2.2.0][build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`) : [:octicons-tag-24: 2.2.0][conan build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in Enables compilation of all the source code, including tests and examples. To support this, it requires some additional Conan build dependencies described in
[Repository Structure and Dependencies](#repository-structure-and-dependencies). [Repository Structure and Dependencies](#repository-structure-and-dependencies).
@ -204,19 +206,19 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list) [`tools.build:skip_test`](https://docs.conan.io/2/reference/commands/config.html?highlight=tools.build:skip_test#conan-config-list)
configuration property is set to `True`). configuration property is set to `True`).
[build all support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [conan build all support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`user.mp-units.build:skip_la`](#user-skip-la){ #user-skip-la } [`user.mp-units.build:skip_la`](#user-skip-la){ #user-skip-la }
: [:octicons-tag-24: 2.2.0][skip la support] · :octicons-milestone-24: `True`/`False` (Default: `False`) : [:octicons-tag-24: 2.2.0][conan skip la support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
If `user.mp-units.build:all` is enabled, among others, Conan installs the external If `user.mp-units.build:all` is enabled, among others, Conan installs the external
[wg21-linear_algebra](https://conan.io/center/recipes/wg21-linear_algebra) [wg21-linear_algebra](https://conan.io/center/recipes/wg21-linear_algebra)
dependency and enables the compilation of linear algebra-based tests and usage examples. dependency and enables the compilation of linear algebra-based tests and usage examples.
Such behavior can be disabled with this option. Such behavior can be disabled with this option.
[skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [conan skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### CMake options ### CMake options
@ -229,16 +231,37 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
[build_cxx_modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [build_cxx_modules support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_STD_FORMAT`](#MP_UNITS_API_STD_FORMAT){ #MP_UNITS_API_STD_FORMAT }
[`MP_UNITS_USE_FMTLIB`](#MP_UNITS_USE_FMTLIB){ #MP_UNITS_USE_FMTLIB } : [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`)
: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) Enables the usage of [`std::format`](https://en.cppreference.com/w/cpp/utility/format/format)
and associated facilities for text formatting. If it is not supported, then
Forces usage of [{fmt}](https://github.com/fmtlib/fmt) library instead of the C++20 Standard the [{fmt}](https://github.com/fmtlib/fmt) library is used instead.
Library features.
[use fmtlib support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0 [use fmtlib support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_STRING_VIEW_RET`](#MP_UNITS_API_STRING_VIEW_RET){ #MP_UNITS_API_STRING_VIEW_RET }
: [:octicons-tag-24: 2.2.0][cmake returning string_view] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`)
Enables returning `std::string_view` from the
[`unit_symbol()`](../users_guide/framework_basics/text_output.md#unit_symbol)
and [`dimension_symbol()`](../users_guide/framework_basics/text_output.md#dimension_symbol)
functions. If this feature is not available, those functions will return
`mp_units::basic_fixed_string<CharT, N>` instead.
[cmake returning string_view]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`MP_UNITS_API_NO_CRTP`](#MP_UNITS_API_NO_CRTP){ #MP_UNITS_API_NO_CRTP }
: [:octicons-tag-24: 2.2.0][cmake no crtp support] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`)
Removes the need for the usage of the CRTP idiom in the
[`quantity_spec` definitions](../users_guide/framework_basics/systems_of_quantities.md#defining-quantities).
[cmake no crtp support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
#### Options for mp-units project developers #### Options for mp-units project developers
[`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } [`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA }

View File

@ -16,27 +16,13 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units)
??? info "Supported compilers" ??? info "Supported compilers"
This library tries to provide the best user experience possible with the C++ language. This library tries to provide the best user experience possible with the C++ language.
To achieve that, it extensively uses C++20 features and the To achieve that, it extensively uses the latest C++ language features.
[explicit object parameter](https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter)
from C++23.
Even though the library benefits from C++23 (if available), C++20 is enough to compile and Even though the library benefits from the latest C++ versions (if available), C++20 is enough
use all of the library's functionality. C++23 features are hidden behind to compile and use all of the library's functionality.
a [preprocessor macro](users_guide/framework_basics/systems_of_quantities.md#defining-quantities)
providing a backward-compatible way to use it.
The below table provides the minimum compiler version required to compile the code using the Please refer to [C++ compiler support chapter](getting_started/cpp_compiler_support.md)
specific feature: for more details.
| Feature | gcc | clang | apple-clang | MSVC |
|----------------------|:---:|:-----:|:-----------:|:----:|
| **Minimum support** | 12 | 16 | 15 | None |
| **`std::format`** | 13 | 17 | None | None |
| **C++ modules** | 14 | 17 | None | None |
| **C++23 extensions** | 14 | 18 | None | None |
More requirements for C++ modules support can be found in the
[CMake's documentation](https://cmake.org/cmake/help/latest/manual/cmake-cxxmodules.7.html).
=== "C++ modules" === "C++ modules"

View File

@ -3,7 +3,7 @@
!!! note !!! note
**mp-units** usage example applications are meant to be built on all of **mp-units** usage example applications are meant to be built on all of
[the supported compilers](../../getting_started/installation_and_usage.md#cpp-compiler-support). [the supported compilers](../../getting_started/cpp_compiler_support.md).
This is why they benefit from the [Wide Compatibility](../use_cases/wide_compatibility.md) mode. This is why they benefit from the [Wide Compatibility](../use_cases/wide_compatibility.md) mode.
!!! tip !!! tip

View File

@ -132,10 +132,15 @@ from such an instantiation.
Quantity specification definitions benefit from an Quantity specification definitions benefit from an
[explicit object parameter](https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter) [explicit object parameter](https://en.cppreference.com/w/cpp/language/member_functions#Explicit_object_parameter)
added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code. added in C++23 to remove the need for CRTP idiom, which significantly simplifies the code.
However, as C++23 is far from being mainstream today, a portability macro `QUANTITY_SPEC()` However, as C++23 is far from being mainstream today,
a [portability macro `QUANTITY_SPEC()`](../use_cases/wide_compatibility.md#QUANTITY_SPEC)
is provided and used consistently through the library to allow the code to compile with C++20 is provided and used consistently through the library to allow the code to compile with C++20
compilers, thanks to the CRTP usage under the hood. compilers, thanks to the CRTP usage under the hood.
See more in the
[C++ compiler support](../../getting_started/cpp_compiler_support.md#explicit-this-parameter)
chapter.
*[CRTP]: Curiously Recurring Template Parameter *[CRTP]: Curiously Recurring Template Parameter
For example, here is how the above quantity kind tree can be modeled in the library: For example, here is how the above quantity kind tree can be modeled in the library:

View File

@ -11,7 +11,7 @@ macros that can be used to ensure the wide compatibility of our code.
!!! note !!! note
Those macros are used in our short [example applications](../examples/tags_index.md) as those are meant Those macros are used in our short [example applications](../examples/tags_index.md) as those are meant
to be built on all of [the supported compilers](../../getting_started/installation_and_usage.md#cpp-compiler-support). to be built on all of [the supported compilers](../../getting_started/cpp_compiler_support.md).
Some still do not support `std::format`, C++ modules, or C++ versions newer than C++20. Some still do not support `std::format`, C++ modules, or C++ versions newer than C++20.
@ -155,5 +155,5 @@ use [fmtlib](https://github.com/fmtlib/fmt) as their primary formatting facility
from additional features provided with the library). from additional features provided with the library).
This macro resolves to either the `std` or `fmt` namespace, depending on the value of This macro resolves to either the `std` or `fmt` namespace, depending on the value of
[MP_UNITS_USE_FMTLIB](../../getting_started/installation_and_usage.md#MP_UNITS_USE_FMTLIB) [MP_UNITS_API_STD_FORMAT](../../getting_started/installation_and_usage.md#MP_UNITS_API_STD_FORMAT)
CMake option. CMake option.

View File

@ -130,6 +130,7 @@ nav:
- Introduction: getting_started/introduction.md - Introduction: getting_started/introduction.md
- Quick Start: getting_started/quick_start.md - Quick Start: getting_started/quick_start.md
- Look and Feel: getting_started/look_and_feel.md - Look and Feel: getting_started/look_and_feel.md
- C++ compiler support (API/ABI): getting_started/cpp_compiler_support.md
- Installation and Usage: getting_started/installation_and_usage.md - Installation and Usage: getting_started/installation_and_usage.md
- FAQ: getting_started/faq.md - FAQ: getting_started/faq.md
- User's Guide: - User's Guide:

View File

@ -31,43 +31,67 @@ include(CheckCXXFeatureSupported)
include(AddMPUnitsModule) include(AddMPUnitsModule)
include(GNUInstallDirs) include(GNUInstallDirs)
# project options # check if libc++ is being used
include(CheckLibcxxInUse)
check_libcxx_in_use(${projectPrefix}LIBCXX)
# project build options
option(${projectPrefix}BUILD_CXX_MODULES "Add C++ modules to the list of default targets" OFF) option(${projectPrefix}BUILD_CXX_MODULES "Add C++ modules to the list of default targets" OFF)
message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}") message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}")
option(${projectPrefix}AS_SYSTEM_HEADERS "Exports library as system headers" OFF) # project API settings
message(STATUS "${projectPrefix}AS_SYSTEM_HEADERS: ${${projectPrefix}AS_SYSTEM_HEADERS}") function(cache_var_values name)
set_property(CACHE ${projectPrefix}${name} PROPERTY STRINGS ${ARGN})
if(NOT ${projectPrefix}${name} IN_LIST ARGN)
message(FATAL_ERROR
"Invalid value '${${projectPrefix}${name}}' provided for a cache variable ${projectPrefix}${name} (${ARGN} allowed)"
)
endif()
message(STATUS "${projectPrefix}${name}: ${${projectPrefix}${name}}")
endfunction()
option(${projectPrefix}USE_FMTLIB "Enables usage of fmtlib instead of the 'std::format' facilities" OFF) set(${projectPrefix}API_STD_FORMAT AUTO CACHE STRING "Enable `std::format` support")
message(STATUS "${projectPrefix}USE_FMTLIB: ${${projectPrefix}USE_FMTLIB}") cache_var_values(API_STD_FORMAT AUTO TRUE FALSE)
set(${projectPrefix}API_STRING_VIEW_RET AUTO CACHE STRING
"Enable returning `std::string_view` from `constexpr` functions"
)
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)
# C++ features # C++ features
check_cxx_feature_supported(__cpp_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) 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) check_cxx_feature_supported("__cpp_constexpr >= 202211L" ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS)
check_cxx_feature_supported(__cpp_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED)
if(${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) # validate settings
message(STATUS "API/ABI: `quantity_spec` uses explicit `this` parameter in its definition") if(${projectPrefix}API_STD_FORMAT STREQUAL "TRUE"
else() AND NOT
message(STATUS "API/ABI: `quantity_spec` uses CRTP in its definition") (${projectPrefix}LIB_FORMAT_SUPPORTED
# libc++ has a basic supports for std::format but does not set __cpp_lib_format
# https://github.com/llvm/llvm-project/issues/77773
OR (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "17"
AND ${projectPrefix}LIBCXX))
)
message(FATAL_ERROR "`std::format` enabled but not supported")
endif() endif()
if(${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) if(${projectPrefix}API_STRING_VIEW_RET STREQUAL "TRUE" AND NOT
message(STATUS "API/ABI: `unit_symbol()` returns `std::string_view`") ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS
else() )
message(STATUS "API/ABI: `unit_symbol()` returns `fixed_string`") message(FATAL_ERROR "`std::string_view` returns enabled but not supported")
endif() endif()
# C++ modules if(${projectPrefix}API_NO_CRTP STREQUAL "TRUE" AND NOT ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED)
if(${projectPrefix}BUILD_CXX_MODULES) message(FATAL_ERROR "`NO_CRTP` mode enabled but explicit `this` parameter is not supported")
if(CMAKE_VERSION VERSION_LESS "3.28.1") endif()
message(FATAL_ERROR, "CMake versions before 3.28.1 do not support C++ modules properly")
if(${projectPrefix}BUILD_CXX_MODULES STREQUAL "TRUE")
if(CMAKE_VERSION VERSION_LESS "3.29")
message(FATAL_ERROR "CMake versions before 3.29 do not support C++ modules properly")
endif() endif()
cmake_minimum_required(VERSION 3.28.1)
# # none of the below seems to work
# cmake_policy(VERSION 3.28.1)
# cmake_policy(SET CMP0155 NEW)
set(${projectPrefix}TARGET_SCOPE "PUBLIC") set(${projectPrefix}TARGET_SCOPE "PUBLIC")
else() else()
set(${projectPrefix}TARGET_SCOPE "INTERFACE") set(${projectPrefix}TARGET_SCOPE "INTERFACE")

View File

@ -0,0 +1,41 @@
# The MIT License (MIT)
#
# Copyright (c) 2018 Mateusz Pusz
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
cmake_minimum_required(VERSION 3.15)
function(check_libcxx_in_use variable)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
message(CHECK_START "Checking if libc++ is being used")
list(APPEND CMAKE_MESSAGE_INDENT " ")
include(CheckCXXSymbolExists)
check_cxx_symbol_exists(_LIBCPP_VERSION "ciso646" ${variable})
set(${variable} ${${variable}} PARENT_SCOPE)
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(${variable})
message(CHECK_PASS "found")
else()
message(CHECK_FAIL "not found")
endif()
endif()
endfunction()

View File

@ -22,6 +22,15 @@
cmake_minimum_required(VERSION 3.23) cmake_minimum_required(VERSION 3.23)
function(set_feature_flag name)
set(val_list "TRUE" "FALSE")
if(${projectPrefix}${name} IN_LIST val_list)
target_compile_definitions(
mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}${name}=$<BOOL:${${projectPrefix}${name}}>
)
endif()
endfunction()
# find dependencies # find dependencies
if(NOT TARGET gsl::gsl-lite) if(NOT TARGET gsl::gsl-lite)
find_package(gsl-lite REQUIRED) find_package(gsl-lite REQUIRED)
@ -78,11 +87,13 @@ add_mp_units_module(
MODULE_INTERFACE_UNIT mp-units-core.cpp MODULE_INTERFACE_UNIT mp-units-core.cpp
) )
target_compile_definitions( set_feature_flag(API_STD_FORMAT)
mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}USE_FMTLIB=$<BOOL:${${projectPrefix}USE_FMTLIB}> set_feature_flag(API_STRING_VIEW_RET)
) set_feature_flag(API_NO_CRTP)
if(${projectPrefix}USE_FMTLIB) if(${projectPrefix}API_STD_FORMAT STREQUAL "FALSE" OR (${projectPrefix}API_STD_FORMAT STREQUAL "AUTO"
AND NOT ${projectPrefix}LIB_FORMAT_SUPPORTED)
)
if(NOT TARGET fmt::fmt) if(NOT TARGET fmt::fmt)
find_package(fmt REQUIRED) find_package(fmt REQUIRED)
endif() endif()

View File

@ -110,3 +110,15 @@
#define MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(X) X #define MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(X) X
#endif #endif
#if !defined MP_UNITS_API_STRING_VIEW_RET && __cpp_constexpr >= 202211L
#define MP_UNITS_API_STRING_VIEW_RET 1
#endif
#if !defined MP_UNITS_API_NO_CRTP && __cpp_explicit_this_parameter
#define MP_UNITS_API_NO_CRTP 1
#endif

View File

@ -22,6 +22,7 @@
#pragma once #pragma once
#include <mp-units/bits/external/hacks.h>
#include <mp-units/bits/dimension_concepts.h> #include <mp-units/bits/dimension_concepts.h>
#include <mp-units/bits/expression_template.h> #include <mp-units/bits/expression_template.h>
#include <mp-units/bits/module_macros.h> #include <mp-units/bits/module_macros.h>
@ -29,7 +30,7 @@
namespace mp_units { namespace mp_units {
MP_UNITS_EXPORT MP_UNITS_EXPORT
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<auto...> template<auto...>
#else #else
template<typename, auto...> template<typename, auto...>
@ -50,7 +51,7 @@ inline constexpr bool is_specialization_of_kind_of<kind_of_<Q>> = true;
template<typename T> template<typename T>
concept QuantityKindSpec = is_specialization_of_kind_of<T>; concept QuantityKindSpec = is_specialization_of_kind_of<T>;
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<auto... Args> template<auto... Args>
void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*); void to_base_specialization_of_quantity_spec(const volatile quantity_spec<Args...>*);
#else #else
@ -65,7 +66,7 @@ inline constexpr bool is_derived_from_specialization_of_quantity_spec =
template<typename T> template<typename T>
inline constexpr bool is_specialization_of_quantity_spec = false; inline constexpr bool is_specialization_of_quantity_spec = false;
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<auto... Args> template<auto... Args>
inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true; inline constexpr bool is_specialization_of_quantity_spec<quantity_spec<Args...>> = true;
#else #else

View File

@ -24,7 +24,7 @@
#include <mp-units/bits/external/hacks.h> #include <mp-units/bits/external/hacks.h>
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
#define QUANTITY_SPEC(name, ...) \ #define QUANTITY_SPEC(name, ...) \
inline constexpr struct name : ::mp_units::quantity_spec<__VA_ARGS__> { \ inline constexpr struct name : ::mp_units::quantity_spec<__VA_ARGS__> { \
@ -38,6 +38,14 @@
#endif #endif
#if !defined MP_UNITS_API_STD_FORMAT || !MP_UNITS_API_STD_FORMAT
#if __has_include(<fmt/format.h>)
#define MP_UNITS_USE_FMTLIB 1
#endif
#endif
#if MP_UNITS_USE_FMTLIB #if MP_UNITS_USE_FMTLIB
MP_UNITS_DIAGNOSTIC_PUSH MP_UNITS_DIAGNOSTIC_PUSH

View File

@ -319,7 +319,7 @@ MP_UNITS_EXPORT template<dimension_symbol_formatting fmt = dimension_symbol_form
return buffer.size(); return buffer.size();
}; };
#if __cpp_constexpr >= 202211L // Permitting static constexpr variables in constexpr functions #if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions
static constexpr std::size_t size = get_size(); static constexpr std::size_t size = get_size();
static constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(D{}); static constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(D{});
return std::string_view(buffer.data(), size); return std::string_view(buffer.data(), size);

View File

@ -24,6 +24,7 @@
#include <mp-units/bits/expression_template.h> #include <mp-units/bits/expression_template.h>
#include <mp-units/bits/external/algorithm.h> #include <mp-units/bits/external/algorithm.h>
#include <mp-units/bits/external/hacks.h>
#include <mp-units/bits/external/type_name.h> #include <mp-units/bits/external/type_name.h>
#include <mp-units/bits/external/type_traits.h> #include <mp-units/bits/external/type_traits.h>
#include <mp-units/bits/get_common_base.h> #include <mp-units/bits/get_common_base.h>
@ -107,11 +108,11 @@ using to_dimension = std::remove_const_t<decltype(Q::dimension)>;
template<AssociatedUnit U> template<AssociatedUnit U>
[[nodiscard]] consteval auto get_associated_quantity(U); [[nodiscard]] consteval auto get_associated_quantity(U);
#ifndef __cpp_explicit_this_parameter #ifndef MP_UNITS_API_NO_CRTP
template<typename Self> template<typename Self>
#endif #endif
struct quantity_spec_interface { struct quantity_spec_interface {
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<typename Self, UnitOf<Self{}> U> template<typename Self, UnitOf<Self{}> U>
[[nodiscard]] consteval Reference auto operator[](this Self self, U u) [[nodiscard]] consteval Reference auto operator[](this Self self, U u)
{ {
@ -166,7 +167,7 @@ MP_UNITS_EXPORT_BEGIN
* types `speed` and `velocity` are considered not equal to `derived_dimension<length, per<time>>` or * types `speed` and `velocity` are considered not equal to `derived_dimension<length, per<time>>` or
* to each other. * to each other.
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<auto...> template<auto...>
#else #else
template<typename, auto...> template<typename, auto...>
@ -211,7 +212,7 @@ MP_UNITS_EXPORT_END
* @tparam BaseDimension base dimension for which a base quantity is being defined * @tparam BaseDimension base dimension for which a base quantity is being defined
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args> template<detail::BaseDimension auto Dim, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>) requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Dim, Args...> : detail::quantity_spec_interface { struct quantity_spec<Dim, Args...> : detail::quantity_spec_interface {
@ -254,7 +255,7 @@ struct quantity_spec<Self, Dim, Args...> : detail::quantity_spec_interface<Self>
* @tparam Eq quantity equation specification of a derived quantity * @tparam Eq quantity equation specification of a derived quantity
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<detail::IntermediateDerivedQuantitySpec auto Eq, one_of<quantity_character> auto... Args> template<detail::IntermediateDerivedQuantitySpec auto Eq, one_of<quantity_character> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>) requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<Eq, Args...> : detail::quantity_spec_interface { struct quantity_spec<Eq, Args...> : detail::quantity_spec_interface {
@ -295,7 +296,7 @@ struct quantity_spec<Self, Eq, Args...> : detail::quantity_spec_interface<Self>
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
* or `is_kind` in case the quantity starts a new hierarchy tree of a kind * or `is_kind` in case the quantity starts a new hierarchy tree of a kind
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args> template<detail::NamedQuantitySpec auto QS, one_of<quantity_character, struct is_kind> auto... Args>
requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>) requires(... && !QuantitySpec<std::remove_const_t<decltype(Args)>>)
struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> { struct quantity_spec<QS, Args...> : std::remove_const_t<decltype(QS)> {
@ -307,7 +308,7 @@ struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
static constexpr auto _parent_ = QS; static constexpr auto _parent_ = QS;
static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character); static constexpr quantity_character character = detail::quantity_character_init<Args...>(QS.character);
#ifndef __cpp_explicit_this_parameter #ifndef MP_UNITS_API_NO_CRTP
template<typename Self_ = Self, UnitOf<Self_{}> U> template<typename Self_ = Self, UnitOf<Self_{}> U>
[[nodiscard]] MP_UNITS_CONSTEVAL Reference auto operator[](U u) const [[nodiscard]] MP_UNITS_CONSTEVAL Reference auto operator[](U u) const
{ {
@ -353,7 +354,7 @@ struct quantity_spec<Self, QS, Args...> : std::remove_const_t<decltype(QS)> {
* @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar * @tparam Args optionally a value of a `quantity_character` in case the base quantity should not be scalar
* or `is_kind` in case the quantity starts a new hierarchy tree of a kind * or `is_kind` in case the quantity starts a new hierarchy tree of a kind
*/ */
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq, template<detail::NamedQuantitySpec auto QS, detail::IntermediateDerivedQuantitySpec auto Eq,
one_of<quantity_character, struct is_kind> auto... Args> one_of<quantity_character, struct is_kind> auto... Args>
requires(!requires { QS._equation_; } || requires(!requires { QS._equation_; } ||
@ -418,7 +419,7 @@ struct quantity_spec<Self, QS, Eq, Args...> : quantity_spec<Self, QS, Args...> {
*/ */
template<detail::IntermediateDerivedQuantitySpecExpr... Expr> template<detail::IntermediateDerivedQuantitySpecExpr... Expr>
struct derived_quantity_spec : struct derived_quantity_spec :
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
detail::quantity_spec_interface, detail::quantity_spec_interface,
#else #else
detail::quantity_spec_interface<derived_quantity_spec<Expr...>>, detail::quantity_spec_interface<derived_quantity_spec<Expr...>>,
@ -456,7 +457,7 @@ template<QuantitySpec Q>
} // namespace detail } // namespace detail
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<typename Q> template<typename Q>
requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{}) requires detail::QuantitySpecWithNoSpecifiers<Q> && (detail::get_kind_tree_root(Q{}) == Q{})
struct kind_of_<Q> : Q { struct kind_of_<Q> : Q {
@ -1430,7 +1431,7 @@ template<QuantitySpec Q>
requires requires(Q q) { get_kind_tree_root(q); } requires requires(Q q) { get_kind_tree_root(q); }
using to_kind = std::remove_const_t<decltype(get_kind_tree_root(Q{}))>; using to_kind = std::remove_const_t<decltype(get_kind_tree_root(Q{}))>;
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
template<NamedQuantitySpec auto QS, auto... Args> template<NamedQuantitySpec auto QS, auto... Args>
[[nodiscard]] consteval bool defined_as_kind(quantity_spec<QS, Args...>) [[nodiscard]] consteval bool defined_as_kind(quantity_spec<QS, Args...>)
#else #else

View File

@ -840,7 +840,7 @@ MP_UNITS_EXPORT template<unit_symbol_formatting fmt = unit_symbol_formatting{},
return buffer.size(); return buffer.size();
}; };
#if __cpp_constexpr >= 202211L // Permitting static constexpr variables in constexpr functions #if MP_UNITS_API_STRING_VIEW_RET // Permitting static constexpr variables in constexpr functions
static constexpr std::size_t size = get_size(); static constexpr std::size_t size = get_size();
static constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(U{}); static constexpr auto buffer = detail::get_symbol_buffer<CharT, size, fmt>(U{});
return std::string_view(buffer.data(), size); return std::string_view(buffer.data(), size);

View File

@ -22,7 +22,7 @@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
if(MP_UNITS_USE_FMTLIB) if(MP_UNITS_API_STD_FORMAT)
find_dependency(fmt) find_dependency(fmt)
endif() endif()

View File

@ -22,13 +22,14 @@
#pragma once #pragma once
#include <mp-units/bits/external/hacks.h>
#include <mp-units/quantity_spec.h> #include <mp-units/quantity_spec.h>
#include <type_traits> #include <type_traits>
template<auto V, typename T> template<auto V, typename T>
inline constexpr bool is_of_type = std::is_same_v<std::remove_cvref_t<decltype(V)>, T>; inline constexpr bool is_of_type = std::is_same_v<std::remove_cvref_t<decltype(V)>, T>;
#ifdef __cpp_explicit_this_parameter #ifdef MP_UNITS_API_NO_CRTP
#define QUANTITY_SPEC_(name, ...) \ #define QUANTITY_SPEC_(name, ...) \
inline constexpr struct name##_ : quantity_spec<__VA_ARGS__> { \ inline constexpr struct name##_ : quantity_spec<__VA_ARGS__> { \

View File

@ -27,4 +27,4 @@ find_package(mp-units REQUIRED)
add_executable(test_package test_package.cpp) add_executable(test_package test_package.cpp)
target_link_libraries(test_package PRIVATE mp-units::mp-units) target_link_libraries(test_package PRIVATE mp-units::mp-units)
target_compile_definitions(test_package PRIVATE MP_UNITS_USE_FMTLIB=$<BOOL:${MP_UNITS_USE_FMTLIB}>) target_compile_definitions(test_package PRIVATE MP_UNITS_API_STD_FORMAT=$<BOOL:${MP_UNITS_API_STD_FORMAT}>)

View File

@ -39,8 +39,8 @@ class TestPackageConan(ConanFile):
def generate(self): def generate(self):
tc = CMakeToolchain(self) tc = CMakeToolchain(self)
tc.variables["MP_UNITS_USE_FMTLIB"] = bool( tc.variables["MP_UNITS_API_STD_FORMAT"] = bool(
self.dependencies["mp-units"].options.use_fmtlib self.dependencies["mp-units"].options.std_format
) )
tc.generate() tc.generate()