diff --git a/.github/workflows/ci-conan.yml b/.github/workflows/ci-conan.yml index b57c088d95..45bcc46521 100644 --- a/.github/workflows/ci-conan.yml +++ b/.github/workflows/ci-conan.yml @@ -221,13 +221,13 @@ jobs: 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 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 shell: bash 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 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 id: get-package-ref shell: bash diff --git a/.github/workflows/ci-test-package-cmake.yml b/.github/workflows/ci-test-package-cmake.yml index 76b8fefa9f..d642e3dab4 100644 --- a/.github/workflows/ci-test-package-cmake.yml +++ b/.github/workflows/ci-test-package-cmake.yml @@ -215,12 +215,12 @@ jobs: 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 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 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 use_fmtlib=${{ env.use_fmtlib }} + -o cxx_modules=${{ matrix.config.cxx_modules }} -o std_format=${{ env.std_format }} mv CMakeUserPresets.json src - name: Configure mp-units CMake if: matrix.config.compiler.type == 'VISUAL' || matrix.config.compiler.type == 'MSVC' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fafd410014..d9fdc3c7ca 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -105,7 +105,7 @@ jobs: conan profile detect --force conan remote add artifactory https://mpusz.jfrog.io/artifactory/api/conan/conan-oss 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 uses: github/codeql-action/analyze@v2 diff --git a/.gitpod.yml b/.gitpod.yml index 17b9902692..7be9af7be3 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -70,17 +70,17 @@ tasks: gp sync-await python-init conan profile detect conan config install $PWD/.gitpod/conan - conan install . -pr gcc12 -o use_fmtlib=True -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 + conan install . -pr gcc12 -o std_format=False -b missing -s build_type=Debug gp sync-done conan-gcc12-20 - conan install . -pr gcc13 -b missing - conan install . -pr gcc13 -b missing -s build_type=Debug + conan install . -pr gcc13 -o std_format=True -b missing + conan install . -pr gcc13 -o std_format=True -b missing -s build_type=Debug gp sync-done conan-gcc13-20 - conan install . -pr clang16 -o use_fmtlib=True -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 + conan install . -pr clang16 -o std_format=False -b missing -s build_type=Debug gp sync-done conan-clang16-20 - conan install . -pr clang17 -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 + conan install . -pr clang17 -o std_format=True -o cxx_modules=True -b missing -s build_type=Debug gp sync-done conan-clang17-20 conan remote login -p $ARTIFACTORY_TOKEN conan-gitpod-mp-units $ARTIFACTORY_USER conan upload "*" -r conan-gitpod-mp-units -c @@ -88,29 +88,29 @@ tasks: init: | gp sync-await conan-gcc12-20 source ${PYTHON_VENV}/bin/activate - conan build . -pr gcc12 -o use_fmtlib=True - conan build . -pr gcc12 -o use_fmtlib=True -s build_type=Debug + conan build . -pr gcc12 -o std_format=False + 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. 🛠️" - name: gcc-13-20 init: | gp sync-await conan-gcc13-20 source ${PYTHON_VENV}/bin/activate - conan build . -pr gcc13 - conan build . -pr gcc13 -s build_type=Debug + conan build . -pr gcc13 -o std_format=True + 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. 🛠️" - name: clang-16-20 init: | gp sync-await conan-clang16-20 source ${PYTHON_VENV}/bin/activate - conan build . -pr clang16 -o use_fmtlib=True - conan build . -pr clang16 -o use_fmtlib=True -s build_type=Debug + conan build . -pr clang16 -o std_format=False + 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. 🛠️" - name: clang-17-20 init: | gp sync-await conan-clang17-20 source ${PYTHON_VENV}/bin/activate - conan build . -pr clang17 -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 + 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. 🛠️" - name: documentation init: | diff --git a/CMakeLists.txt b/CMakeLists.txt index e5ca6de32c..8478cf15a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,8 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") set(projectPrefix MP_UNITS_) -option(${projectPrefix}BUILD_LA "Build code depending on the linear algebra library" ON) -message(STATUS "${projectPrefix}BUILD_LA: ${${projectPrefix}BUILD_LA}") +option(${projectPrefix}DEV_BUILD_LA "Build code depending on the linear algebra library" ON) +message(STATUS "${projectPrefix}DEV_BUILD_LA: ${${projectPrefix}DEV_BUILD_LA}") # make sure that the file is being used as an entry point include(modern_project_structure) @@ -43,9 +43,9 @@ include(warnings) set_warnings() # enable include-what-you-use -option(${projectPrefix}IWYU "Enables include-what-you-use" OFF) +option(${projectPrefix}DEV_IWYU "Enables include-what-you-use" OFF) -if(${projectPrefix}IWYU) +if(${projectPrefix}DEV_IWYU) include(include-what-you-use) enable_iwyu( MAPPING_FILE "${PROJECT_SOURCE_DIR}/.mp-units.imp" @@ -53,10 +53,6 @@ if(${projectPrefix}IWYU) MAX_LINE_LENGTH 120 NO_COMMENTS ) - - if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(${projectPrefix}AS_SYSTEM_HEADERS ON) - endif() endif() # enable_clang_tidy() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ae27af60f4..d156d27a49 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. 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 -[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. diff --git a/conanfile.py b/conanfile.py index f07f70a676..2a83b59647 100644 --- a/conanfile.py +++ b/conanfile.py @@ -25,13 +25,20 @@ from conan import ConanFile 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.files import copy, load, rmdir 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): name = "mp-units" homepage = "https://github.com/mpusz/mp-units" @@ -54,14 +61,18 @@ class MPUnitsConan(ConanFile): url = "https://github.com/mpusz/mp-units" settings = "os", "arch", "compiler", "build_type" options = { - "cxx_modules": [True, False], - "use_fmtlib": [True, False], + "cxx_modules": ["auto", True, False], + "std_format": ["auto", True, False], + "string_view_ret": ["auto", True, False], + "no_crtp": ["auto", True, False], } default_options = { - "cxx_modules": False, - "use_fmtlib": False, + "cxx_modules": "auto", + "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_sources = ( "docs/*", @@ -75,27 +86,106 @@ class MPUnitsConan(ConanFile): no_copy_source = True @property - def _minimum_compilers_version(self): + def _feature_compatibility(self): return { - "gcc": "12", - "clang": "16", - "apple-clang": "15", - # , "msvc": "192" + "minimum_support": { + "std": "20", + "compiler": { + "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 - def _std_format_minimum_compilers_version(self): + def _option_feature_map(self): return { - "gcc": "13", - "clang": "17", - # , "apple-clang": "15" - # , "msvc": "192" + "std_format": "std_format", + "cxx_modules": "cxx_modules", + "string_view_ret": "static_constexpr_vars_in_constexpr_func", + "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 def _build_all(self): 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 def _skip_la(self): return bool(self.conf.get("user.mp-units.build:skip_la", default=False)) @@ -109,7 +199,7 @@ def set_version(self): def requirements(self): self.requires("gsl-lite/0.41.0") - if self.options.use_fmtlib: + if self._use_fmtlib: self.requires("fmt/10.2.1") def build_requirements(self): @@ -119,27 +209,10 @@ def build_requirements(self): self.test_requires("wg21-linear_algebra/0.7.3") def validate(self): - check_min_cppstd(self, "20") - - 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] - - 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." - ) + self._check_feature_supported("mp-units", "minimum_support") + for key, value in self._option_feature_map.items(): + if self.options.get_safe(key) is True: + self._check_feature_supported(key, value) def layout(self): cmake_layout(self) @@ -148,11 +221,19 @@ def generate(self): tc = CMakeToolchain(self) if self._build_all: 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["MP_UNITS_BUILD_CXX_MODULES"] = True - tc.cache_variables["MP_UNITS_BUILD_LA"] = self._build_all and not self._skip_la - tc.cache_variables["MP_UNITS_USE_FMTLIB"] = bool(self.options.use_fmtlib) + tc.cache_variables["MP_UNITS_BUILD_CXX_MODULES"] = str( + self.options.cxx_modules + ).upper() + tc.cache_variables["MP_UNITS_API_STD_FORMAT"] = str( + 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() deps = CMakeDeps(self) deps.generate() @@ -192,7 +273,7 @@ def package_info(self): self.cpp_info.builddirs = ["."] else: self.cpp_info.components["core"].requires = ["gsl-lite::gsl-lite"] - if self.options.use_fmtlib: + if self._use_fmtlib: self.cpp_info.components["core"].requires.append("fmt::fmt") if compiler == "msvc": self.cpp_info.components["core"].cxxflags = ["/utf-8"] diff --git a/docs/getting_started/cpp_compiler_support.md b/docs/getting_started/cpp_compiler_support.md new file mode 100644 index 0000000000..73f1c6c6c2 --- /dev/null +++ b/docs/getting_started/cpp_compiler_support.md @@ -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` 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 diff --git a/docs/getting_started/installation_and_usage.md b/docs/getting_started/installation_and_usage.md index d741b57946..aba8cc37b0 100644 --- a/docs/getting_started/installation_and_usage.md +++ b/docs/getting_started/installation_and_usage.md @@ -3,60 +3,12 @@ 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. -## 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 This repository contains three independent CMake-based projects: -- _./src_ +- **_./src_** - header-only project containing whole **mp-units** library - _./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 (if `std::format` is not supported yet on a specific compiler). -- _._ +- **_._** - project used as an **entry point for library development and CI/CD** - 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 and tests. -- *./test_package* +- **_./test_package_** - 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). +## 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 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: ```shell -pip3 install -U conan +pip install -U conan ``` 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 +!!! 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 [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. - [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 - Library features. + 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 + 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` 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 [`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 [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) 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 } -: [: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 [wg21-linear_algebra](https://conan.io/center/recipes/wg21-linear_algebra) dependency and enables the compilation of linear algebra-based tests and usage examples. 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 @@ -229,18 +231,40 @@ 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 +[`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: `ON`/`OFF` (Default: `OFF`) +: [:octicons-tag-24: 2.2.0][use fmtlib support] · :octicons-milestone-24: `AUTO`/`ON`/`OFF` (Default: `AUTO`) - Forces usage of [{fmt}](https://github.com/fmtlib/fmt) library instead of the C++20 Standard - Library features. + 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 + the [{fmt}](https://github.com/fmtlib/fmt) library is used instead. [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` 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 -[`MP_UNITS_BUILD_LA`](#MP_UNITS_BUILD_LA){ #MP_UNITS_BUILD_LA } +[`MP_UNITS_DEV_BUILD_LA`](#MP_UNITS_DEV_BUILD_LA){ #MP_UNITS_DEV_BUILD_LA } : [:octicons-tag-24: 2.0.0][build la support] · :octicons-milestone-24: `ON`/`OFF` (Default: `ON`) @@ -249,12 +273,11 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"} [build la support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0 -[`MP_UNITS_IWYU`](#MP_UNITS_IWYU){ #MP_UNITS_IWYU } +[`MP_UNITS_DEV_IWYU`](#MP_UNITS_DEV_IWYU){ #MP_UNITS_DEV_IWYU } : [:octicons-tag-24: 2.0.0][iwyu support] · :octicons-milestone-24: `ON`/`OFF` (Default: `OFF`) Enables `include-what-you-use` when compiling with a clang compiler. - Additionally turns on [`MP_UNITS_AS_SYSTEM_HEADERS`](#MP_UNITS_AS_SYSTEM_HEADERS). [iwyu support]: https://github.com/mpusz/mp-units/releases/tag/v2.0.0 diff --git a/docs/index.md b/docs/index.md index 4f98b54be1..dcf78bf89c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,27 +16,13 @@ The library source code is hosted on [GitHub](https://github.com/mpusz/mp-units) ??? info "Supported compilers" 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 - [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). + 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. + + Please refer to [C++ compiler support chapter](getting_started/cpp_compiler_support.md) + for more details. === "C++ modules" diff --git a/docs/users_guide/examples/tags_index.md b/docs/users_guide/examples/tags_index.md index 0f1d4da8c3..8d316e4e43 100644 --- a/docs/users_guide/examples/tags_index.md +++ b/docs/users_guide/examples/tags_index.md @@ -3,7 +3,7 @@ !!! note **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. !!! tip diff --git a/docs/users_guide/framework_basics/systems_of_quantities.md b/docs/users_guide/framework_basics/systems_of_quantities.md index f7e88d6449..65605f2bbf 100644 --- a/docs/users_guide/framework_basics/systems_of_quantities.md +++ b/docs/users_guide/framework_basics/systems_of_quantities.md @@ -132,10 +132,15 @@ from such an instantiation. Quantity specification definitions benefit from an [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. - 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 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 For example, here is how the above quantity kind tree can be modeled in the library: diff --git a/docs/users_guide/use_cases/wide_compatibility.md b/docs/users_guide/use_cases/wide_compatibility.md index 6311103dc6..ef1b986947 100644 --- a/docs/users_guide/use_cases/wide_compatibility.md +++ b/docs/users_guide/use_cases/wide_compatibility.md @@ -11,7 +11,7 @@ macros that can be used to ensure the wide compatibility of our code. !!! note 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. @@ -155,5 +155,5 @@ use [fmtlib](https://github.com/fmtlib/fmt) as their primary formatting facility from additional features provided with the library). 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. diff --git a/mkdocs.yml b/mkdocs.yml index e43ba05e26..9e4283c061 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -130,6 +130,7 @@ nav: - Introduction: getting_started/introduction.md - Quick Start: getting_started/quick_start.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 - FAQ: getting_started/faq.md - User's Guide: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3515f77049..6ca25bdae9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -31,43 +31,67 @@ include(CheckCXXFeatureSupported) include(AddMPUnitsModule) 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) message(STATUS "${projectPrefix}BUILD_CXX_MODULES: ${${projectPrefix}BUILD_CXX_MODULES}") -option(${projectPrefix}AS_SYSTEM_HEADERS "Exports library as system headers" OFF) -message(STATUS "${projectPrefix}AS_SYSTEM_HEADERS: ${${projectPrefix}AS_SYSTEM_HEADERS}") +# project API settings +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() + +set(${projectPrefix}API_STD_FORMAT AUTO CACHE STRING "Enable `std::format` support") +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) -option(${projectPrefix}USE_FMTLIB "Enables usage of fmtlib instead of the 'std::format' facilities" OFF) -message(STATUS "${projectPrefix}USE_FMTLIB: ${${projectPrefix}USE_FMTLIB}") +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 -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_explicit_this_parameter ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) -if(${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) - message(STATUS "API/ABI: `quantity_spec` uses explicit `this` parameter in its definition") -else() - message(STATUS "API/ABI: `quantity_spec` uses CRTP in its definition") +# validate settings +if(${projectPrefix}API_STD_FORMAT STREQUAL "TRUE" + AND NOT + (${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() -if(${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS) - message(STATUS "API/ABI: `unit_symbol()` returns `std::string_view`") -else() - message(STATUS "API/ABI: `unit_symbol()` returns `fixed_string`") +if(${projectPrefix}API_STRING_VIEW_RET STREQUAL "TRUE" AND NOT + ${projectPrefix}STATIC_CONSTEXPR_VARS_IN_CONSTEXPR_FUNCTIONS +) + message(FATAL_ERROR "`std::string_view` returns enabled but not supported") endif() -# C++ modules -if(${projectPrefix}BUILD_CXX_MODULES) - if(CMAKE_VERSION VERSION_LESS "3.28.1") - message(FATAL_ERROR, "CMake versions before 3.28.1 do not support C++ modules properly") - 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) +if(${projectPrefix}API_NO_CRTP STREQUAL "TRUE" AND NOT ${projectPrefix}EXPLICIT_THIS_PARAMETER_SUPPORTED) + message(FATAL_ERROR "`NO_CRTP` mode enabled but explicit `this` parameter is not supported") +endif() +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() set(${projectPrefix}TARGET_SCOPE "PUBLIC") else() set(${projectPrefix}TARGET_SCOPE "INTERFACE") diff --git a/src/cmake/CheckLibcxxInUse.cmake b/src/cmake/CheckLibcxxInUse.cmake new file mode 100644 index 0000000000..7335da5c0c --- /dev/null +++ b/src/cmake/CheckLibcxxInUse.cmake @@ -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() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 0be0f69604..329ec1ec2b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -22,6 +22,15 @@ 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}=$ + ) + endif() +endfunction() + # find dependencies if(NOT TARGET gsl::gsl-lite) find_package(gsl-lite REQUIRED) @@ -78,11 +87,13 @@ add_mp_units_module( MODULE_INTERFACE_UNIT mp-units-core.cpp ) -target_compile_definitions( - mp-units-core ${${projectPrefix}TARGET_SCOPE} ${projectPrefix}USE_FMTLIB=$ -) +set_feature_flag(API_STD_FORMAT) +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) find_package(fmt REQUIRED) endif() diff --git a/src/core/include/mp-units/bits/external/hacks.h b/src/core/include/mp-units/bits/external/hacks.h index 788aea1186..38e729d9e3 100644 --- a/src/core/include/mp-units/bits/external/hacks.h +++ b/src/core/include/mp-units/bits/external/hacks.h @@ -110,3 +110,15 @@ #define MP_UNITS_CONSTRAINED_NTTP_WORKAROUND(X) X #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 diff --git a/src/core/include/mp-units/bits/quantity_spec_concepts.h b/src/core/include/mp-units/bits/quantity_spec_concepts.h index 5897d3d153..d04e8bd7c5 100644 --- a/src/core/include/mp-units/bits/quantity_spec_concepts.h +++ b/src/core/include/mp-units/bits/quantity_spec_concepts.h @@ -24,12 +24,13 @@ #include #include +#include #include namespace mp_units { MP_UNITS_EXPORT -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template #else template @@ -50,7 +51,7 @@ inline constexpr bool is_specialization_of_kind_of> = true; template concept QuantityKindSpec = is_specialization_of_kind_of; -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template void to_base_specialization_of_quantity_spec(const volatile quantity_spec*); #else @@ -65,7 +66,7 @@ inline constexpr bool is_derived_from_specialization_of_quantity_spec = template inline constexpr bool is_specialization_of_quantity_spec = false; -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template inline constexpr bool is_specialization_of_quantity_spec> = true; #else diff --git a/src/core/include/mp-units/compat_macros.h b/src/core/include/mp-units/compat_macros.h index 25666a0d33..24843dd4a7 100644 --- a/src/core/include/mp-units/compat_macros.h +++ b/src/core/include/mp-units/compat_macros.h @@ -24,7 +24,7 @@ #include -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP #define QUANTITY_SPEC(name, ...) \ inline constexpr struct name : ::mp_units::quantity_spec<__VA_ARGS__> { \ @@ -38,6 +38,14 @@ #endif +#if !defined MP_UNITS_API_STD_FORMAT || !MP_UNITS_API_STD_FORMAT + +#if __has_include() +#define MP_UNITS_USE_FMTLIB 1 +#endif + +#endif + #if MP_UNITS_USE_FMTLIB MP_UNITS_DIAGNOSTIC_PUSH diff --git a/src/core/include/mp-units/dimension.h b/src/core/include/mp-units/dimension.h index 43e5f3ced0..c84da48aea 100644 --- a/src/core/include/mp-units/dimension.h +++ b/src/core/include/mp-units/dimension.h @@ -319,7 +319,7 @@ MP_UNITS_EXPORT template= 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 auto buffer = detail::get_symbol_buffer(D{}); return std::string_view(buffer.data(), size); diff --git a/src/core/include/mp-units/quantity_spec.h b/src/core/include/mp-units/quantity_spec.h index 4b54c64714..3679b19eaa 100644 --- a/src/core/include/mp-units/quantity_spec.h +++ b/src/core/include/mp-units/quantity_spec.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -107,11 +108,11 @@ using to_dimension = std::remove_const_t; template [[nodiscard]] consteval auto get_associated_quantity(U); -#ifndef __cpp_explicit_this_parameter +#ifndef MP_UNITS_API_NO_CRTP template #endif struct quantity_spec_interface { -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template 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>` or * to each other. */ -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template #else template @@ -211,7 +212,7 @@ MP_UNITS_EXPORT_END * @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 */ -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(... && !QuantitySpec>) struct quantity_spec : detail::quantity_spec_interface { @@ -254,7 +255,7 @@ struct quantity_spec : detail::quantity_spec_interface * @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 */ -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(... && !QuantitySpec>) struct quantity_spec : detail::quantity_spec_interface { @@ -295,7 +296,7 @@ struct quantity_spec : detail::quantity_spec_interface * @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 */ -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(... && !QuantitySpec>) struct quantity_spec : std::remove_const_t { @@ -307,7 +308,7 @@ struct quantity_spec : std::remove_const_t { static constexpr auto _parent_ = QS; static constexpr quantity_character character = detail::quantity_character_init(QS.character); -#ifndef __cpp_explicit_this_parameter +#ifndef MP_UNITS_API_NO_CRTP template U> [[nodiscard]] MP_UNITS_CONSTEVAL Reference auto operator[](U u) const { @@ -353,7 +354,7 @@ struct quantity_spec : std::remove_const_t { * @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 */ -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template auto... Args> requires(!requires { QS._equation_; } || @@ -418,7 +419,7 @@ struct quantity_spec : quantity_spec { */ template struct derived_quantity_spec : -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP detail::quantity_spec_interface, #else detail::quantity_spec_interface>, @@ -456,7 +457,7 @@ template } // namespace detail -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template requires detail::QuantitySpecWithNoSpecifiers && (detail::get_kind_tree_root(Q{}) == Q{}) struct kind_of_ : Q { @@ -1430,7 +1431,7 @@ template requires requires(Q q) { get_kind_tree_root(q); } using to_kind = std::remove_const_t; -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP template [[nodiscard]] consteval bool defined_as_kind(quantity_spec) #else diff --git a/src/core/include/mp-units/unit.h b/src/core/include/mp-units/unit.h index 46ec19ebf0..5c9fdf9a27 100644 --- a/src/core/include/mp-units/unit.h +++ b/src/core/include/mp-units/unit.h @@ -840,7 +840,7 @@ MP_UNITS_EXPORT template= 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 auto buffer = detail::get_symbol_buffer(U{}); return std::string_view(buffer.data(), size); diff --git a/src/mp-unitsConfig.cmake b/src/mp-unitsConfig.cmake index 084bd61d8e..b42e3b5585 100644 --- a/src/mp-unitsConfig.cmake +++ b/src/mp-unitsConfig.cmake @@ -22,7 +22,7 @@ include(CMakeFindDependencyMacro) -if(MP_UNITS_USE_FMTLIB) +if(NOT MP_UNITS_API_STD_FORMAT) find_dependency(fmt) endif() diff --git a/test/runtime/CMakeLists.txt b/test/runtime/CMakeLists.txt index 897957a988..f78c0ad4f5 100644 --- a/test/runtime/CMakeLists.txt +++ b/test/runtime/CMakeLists.txt @@ -30,7 +30,7 @@ if(${projectPrefix}BUILD_CXX_MODULES) endif() target_link_libraries(unit_tests_runtime PRIVATE mp-units::mp-units Catch2::Catch2WithMain) -if(${projectPrefix}BUILD_LA) +if(${projectPrefix}DEV_BUILD_LA) find_package(wg21_linear_algebra REQUIRED) target_sources(unit_tests_runtime PRIVATE linear_algebra_test.cpp) target_link_libraries(unit_tests_runtime PRIVATE wg21_linear_algebra::wg21_linear_algebra) diff --git a/test/static/test_tools.h b/test/static/test_tools.h index 42cbc61e50..2c9bf801ae 100644 --- a/test/static/test_tools.h +++ b/test/static/test_tools.h @@ -22,13 +22,14 @@ #pragma once +#include #include #include template inline constexpr bool is_of_type = std::is_same_v, T>; -#ifdef __cpp_explicit_this_parameter +#ifdef MP_UNITS_API_NO_CRTP #define QUANTITY_SPEC_(name, ...) \ inline constexpr struct name##_ : quantity_spec<__VA_ARGS__> { \ diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt index b31bd07b5e..cf00e42685 100644 --- a/test_package/CMakeLists.txt +++ b/test_package/CMakeLists.txt @@ -31,11 +31,11 @@ find_package(mp-units REQUIRED) add_executable(test_package-headers test_package.cpp) target_compile_features(test_package-headers PRIVATE cxx_std_20) target_link_libraries(test_package-headers PRIVATE mp-units::mp-units) -target_compile_definitions(test_package-headers PRIVATE MP_UNITS_USE_FMTLIB=$) +target_compile_definitions(test_package-headers PRIVATE MP_UNITS_API_STD_FORMAT=$) if(MP_UNITS_BUILD_CXX_MODULES) add_executable(test_package test_package.cpp) target_compile_features(test_package PUBLIC cxx_std_20) target_link_libraries(test_package PRIVATE mp-units::mp-units) - target_compile_definitions(test_package PRIVATE MP_UNITS_MODULES MP_UNITS_USE_FMTLIB=$) + target_compile_definitions(test_package PRIVATE MP_UNITS_MODULES MP_UNITS_API_STD_FORMAT=$) endif() diff --git a/test_package/conanfile.py b/test_package/conanfile.py index 9ca6e0bca2..28ffefcf82 100644 --- a/test_package/conanfile.py +++ b/test_package/conanfile.py @@ -41,8 +41,8 @@ def generate(self): tc = CMakeToolchain(self) if self.dependencies["mp-units"].options.cxx_modules: tc.variables["MP_UNITS_BUILD_CXX_MODULES"] = True - tc.variables["MP_UNITS_USE_FMTLIB"] = bool( - self.dependencies["mp-units"].options.use_fmtlib + tc.variables["MP_UNITS_API_STD_FORMAT"] = bool( + self.dependencies["mp-units"].options.std_format ) tc.generate()