Merge branch 'master' of github.com:mpusz/mp-units

This commit is contained in:
Mateusz Pusz
2024-02-16 15:46:44 +01:00
21 changed files with 254 additions and 188 deletions

View File

@ -47,7 +47,7 @@ jobs:
# name: "MSVC 14.2",
# os: windows-2019,
# compiler: { type: VISUAL, version: 16, cc: "", cxx: "" },
# conan-config: "-c user.build:skip_la=True",
# conan-config: "-c user.mp-units.build:skip_la=True",
# }
# - {
# name: "MSVC 14.3",
@ -212,7 +212,7 @@ jobs:
run: |
conan create . --user mpusz --channel ${CHANNEL} --lockfile-out=package.lock \
-b mp-units/* -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" \
-c user.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 use_fmtlib=${{ env.use_fmtlib }} ${{ matrix.config.conan-config }}
- name: Obtain package reference
id: get-package-ref
shell: bash

View File

@ -205,7 +205,7 @@ jobs:
- name: Install Conan dependencies
shell: bash
run: |
conan install . -b missing -c tools.cmake.cmaketoolchain:generator="Ninja Multi-Config" -c user.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 }}
mv CMakeUserPresets.json src
- name: Configure mp-units CMake

View File

@ -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.build:all=True -o use_fmtlib=True -b missing
conan build .. -s compiler.cppstd=20 -c user.mp-units.build:all=True -o use_fmtlib=True -b missing
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@ -70,17 +70,17 @@ tasks:
gp sync-await python-init
conan profile detect
conan config install $PWD/.gitpod/conan
conan install . -pr gcc12 -c user.build:all=True -o use_fmtlib=True -b missing
conan install . -pr gcc12 -c user.build:all=True -o use_fmtlib=True -b missing -s build_type=Debug
conan install . -pr gcc12 -o use_fmtlib=True -b missing
conan install . -pr gcc12 -o use_fmtlib=True -b missing -s build_type=Debug
gp sync-done conan-gcc12-20
conan install . -pr gcc13 -c user.build:all=True -b missing
conan install . -pr gcc13 -c user.build:all=True -b missing -s build_type=Debug
conan install . -pr gcc13 -b missing
conan install . -pr gcc13 -b missing -s build_type=Debug
gp sync-done conan-gcc13-20
conan install . -pr clang16 -c user.build:all=True -o use_fmtlib=True -b missing
conan install . -pr clang16 -c user.build:all=True -o use_fmtlib=True -b missing -s build_type=Debug
conan install . -pr clang16 -o use_fmtlib=True -b missing
conan install . -pr clang16 -o use_fmtlib=True -b missing -s build_type=Debug
gp sync-done conan-clang16-20
conan install . -pr clang17 -c user.build:all=True -o cxx_modules=True -b missing
conan install . -pr clang17 -c user.build:all=True -o cxx_modules=True -b missing -s build_type=Debug
conan install . -pr clang17 -o cxx_modules=True -b missing
conan install . -pr clang17 -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 -c user.build:all=True -o use_fmtlib=True
conan build . -pr gcc12 -c user.build:all=True -o use_fmtlib=True -s build_type=Debug
conan build . -pr gcc12 -o use_fmtlib=True
conan build . -pr gcc12 -o use_fmtlib=True -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 -c user.build:all=True
conan build . -pr gcc13 -c user.build:all=True -s build_type=Debug
conan build . -pr gcc13
conan build . -pr gcc13 -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 -c user.build:all=True -o use_fmtlib=True
conan build . -pr clang16 -c user.build:all=True -o use_fmtlib=True -s build_type=Debug
conan build . -pr clang16 -o use_fmtlib=True
conan build . -pr clang16 -o use_fmtlib=True -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 -c user.build:all=True -o cxx_modules=True
conan build . -pr clang17 -c user.build:all=True -o cxx_modules=True -s build_type=Debug
conan build . -pr clang17 -o cxx_modules=True
conan build . -pr clang17 -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: |

View File

@ -1,2 +1,3 @@
tools.cmake.cmaketoolchain:generator=Ninja Multi-Config
tools.cmake.cmake_layout:build_folder_vars=["settings.compiler", "settings.compiler.version", "settings.compiler.cppstd"]
user.mp-units.build:all=True

View File

@ -56,13 +56,21 @@ After the script is done please make sure to stage all those changes to git comm
The simplest way to verify if all targets build correctly and all unit tests pass is to run:
```bash
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o cxx_modules=True -c user.build:all=True -b missing
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o cxx_modules=True -c user.mp-units.build:all=True -b missing
```
as described in the
[Installation and Usage](https://mpusz.github.io/mp-units/latest/getting_started/installation_and_usage/#contributing-or-just-building-all-the-tests-and-examples)
chapter of our documentation.
_Hint:_ To ensure that that we always build all the targets and to save some typing of the Conan commands,
it is a good practice to set the following in the `~/.conan2/global.conf`:
```text
user.mp-units.build:all=True
```
### Backward Compatibility
Before submission, please remember to check if the code compiles fine on the supported compilers.

View File

@ -83,7 +83,7 @@ class MPUnitsConan(ConanFile):
return {
"gcc": "12",
"clang": "16",
"apple-clang": "15"
"apple-clang": "15",
# , "msvc": "192"
}
@ -91,18 +91,18 @@ class MPUnitsConan(ConanFile):
def _std_format_minimum_compilers_version(self):
return {
"gcc": "13",
"clang": "17"
"clang": "17",
# , "apple-clang": "15"
# , "msvc": "192"
}
@property
def _build_all(self):
return bool(self.conf.get("user.build:all", default=False))
return bool(self.conf.get("user.mp-units.build:all", default=False))
@property
def _skip_la(self):
return bool(self.conf.get("user.build:skip_la", default=False))
return bool(self.conf.get("user.mp-units.build:skip_la", default=False))
def set_version(self):
content = load(self, os.path.join(self.recipe_folder, "src/CMakeLists.txt"))

View File

@ -194,9 +194,9 @@ tools.build:compiler_executables={"c": "gcc-12", "cpp": "g++-12"}
### Conan configuration properties
[`user.build:all`](#user-build-all){ #user-build-all }
[`user.mp-units.build:all`](#user.mp-units.build-all){ #user.mp-units.build-all }
: [:octicons-tag-24: 0.8.0][build all support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
: [:octicons-tag-24: 2.2.0][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 +204,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/v0.8.0
[build all support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
[`user.build:skip_la`](#user-skip-la){ #user-skip-la }
[`user.mp-units.build:skip_la`](#user-skip-la){ #user-skip-la }
: [:octicons-tag-24: 0.8.0][skip la support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
: [:octicons-tag-24: 2.2.0][skip la support] · :octicons-milestone-24: `True`/`False` (Default: `False`)
If `user.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)
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/v0.8.0
[skip la support]: https://github.com/mpusz/mp-units/releases/tag/v2.2.0
### CMake options
@ -453,11 +453,11 @@ In case you would like to build all the **mp-units** source code (with unit test
you should:
1. Use the _CMakeLists.txt_ from the top-level directory.
2. Run Conan with [`user.build:all`](#user-build-all) = `True`.
2. Run Conan with [`user.mp-units.build:all`](#user.mp-units.build-all) = `True`.
```shell
git clone https://github.com/mpusz/mp-units.git && cd units
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o cxx_modules=True -c user.build:all=True -b missing
conan build . -pr <your_conan_profile> -s compiler.cppstd=23 -o cxx_modules=True -c user.mp-units.build:all=True -b missing
```
The above will download and install all of the dependencies needed for the development of the library,
@ -504,7 +504,7 @@ After that, you can either:
To test CMake installation and Conan packaging or create a Conan package run:
```shell
conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=20 -o cxx_modules=True -c user.build:all=True -b missing
conan create . --user <username> --channel <channel> -pr <your_conan_profile> -s compiler.cppstd=20 -o cxx_modules=True -c user.mp-units.build:all=True -b missing
```
The above will create a Conan package and run tests provided in _./test_package_ directory.

View File

@ -392,4 +392,9 @@ static_assert(same_type<kind_of<isq::length> / isq::time, isq::length / isq::tim
Only a root quantity from the hierarchy tree or the one marked with `is_kind` specifier
in the `quantity_spec` definition can be put as a template parameter to the `kind_of`
specifier. For example, `kind_of<isq::width>` will fail to compile.
specifier. For example, `kind_of<isq::width>` will fail to compile. However, we can call
`get_kind(q)` to obtain a kind of any quantity:
```cpp
static_assert(get_kind(isq::width) == kind_of<isq::length>);
```

View File

@ -50,13 +50,13 @@ QuantityOf<isq::mechanical_energy> auto total_energy(QuantityOf<isq::momentum> a
void si_example()
{
using namespace mp_units::si::unit_symbols;
constexpr auto GeV = si::giga<si::electronvolt>;
constexpr QuantityOf<isq::speed> auto c = 1. * si::si2019::speed_of_light_in_vacuum;
auto c2 = pow<2>(c);
constexpr Unit auto GeV = si::giga<si::electronvolt>;
constexpr quantity c = 1. * si::si2019::speed_of_light_in_vacuum;
quantity c2 = pow<2>(c);
const auto p1 = isq::momentum(4. * GeV / c);
const quantity p1 = isq::momentum(4. * GeV / c);
const QuantityOf<isq::mass> auto m1 = 3. * GeV / c2;
const auto E = total_energy(p1, m1, c);
const quantity E = total_energy(p1, m1, c);
std::cout << "\n*** SI units (c = " << c << " = " << c.in(si::metre / s) << ") ***\n";
@ -65,18 +65,18 @@ void si_example()
<< "m = " << m1 << "\n"
<< "E = " << E << "\n";
const auto p2 = p1.in(GeV / (m / s));
const auto m2 = m1.in(GeV / pow<2>(m / s));
const auto E2 = total_energy(p2, m2, c).in(GeV);
const quantity p2 = p1.in(GeV / (m / s));
const quantity m2 = m1.in(GeV / pow<2>(m / s));
const quantity E2 = total_energy(p2, m2, c).in(GeV);
std::cout << "\n[in `GeV`]\n"
<< "p = " << p2 << "\n"
<< "m = " << m2 << "\n"
<< "E = " << E2 << "\n";
const auto p3 = p1.in(kg * m / s);
const auto m3 = m1.in(kg);
const auto E3 = total_energy(p3, m3, c).in(J);
const quantity p3 = p1.in(kg * m / s);
const quantity m3 = m1.in(kg);
const quantity E3 = total_energy(p3, m3, c).in(J);
std::cout << "\n[in SI base units]\n"
<< "p = " << p3 << "\n"
@ -92,10 +92,10 @@ void natural_example()
using namespace mp_units::natural;
using namespace mp_units::natural::unit_symbols;
constexpr auto c = 1. * speed_of_light;
const auto p = 4. * momentum[GeV];
const auto m = 3. * mass[GeV];
const auto E = total_energy(p, m, c);
constexpr quantity c = 1. * speed_of_light;
const quantity p = 4. * momentum[GeV];
const quantity m = 3. * mass[GeV];
const quantity E = total_energy(p, m, c);
std::cout << "\n*** Natural units (c = " << c << ") ***\n"
<< "p = " << p << "\n"

View File

@ -83,7 +83,7 @@
// TODO revise the below when clang-18 is released
#if MP_UNITS_COMP_CLANG >= 18 && !defined __cpp_explicit_this_parameter
#define __cpp_explicit_this_parameter
#define __cpp_explicit_this_parameter 202110L
#endif

View File

@ -130,7 +130,7 @@ concept QuantitySpec =
detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T> || detail::QuantityKindSpec<T>;
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q);
[[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q);
namespace detail {
@ -138,7 +138,7 @@ template<auto To, auto From>
concept NestedQuantityKindSpecOf =
QuantitySpec<std::remove_const_t<decltype(From)>> && QuantitySpec<std::remove_const_t<decltype(To)>> &&
get_kind(From) != get_kind(To) &&
std::derived_from<std::remove_cvref_t<decltype(To)>, std::remove_cvref_t<decltype(get_kind(From))>>;
std::derived_from<std::remove_cvref_t<decltype(To)>, std::remove_cvref_t<decltype(get_kind(From)._quantity_spec_)>>;
}

View File

@ -444,11 +444,15 @@ namespace detail {
template<typename T>
concept QuantitySpecWithNoSpecifiers = detail::NamedQuantitySpec<T> || detail::IntermediateDerivedQuantitySpec<T>;
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind_tree_root(Q q);
} // namespace detail
#ifdef __cpp_explicit_this_parameter
template<auto Q>
requires(detail::QuantitySpecWithNoSpecifiers<std::remove_const_t<decltype(Q)>>) && (get_kind(Q) == Q)
requires(detail::QuantitySpecWithNoSpecifiers<std::remove_const_t<decltype(Q)>>) &&
(detail::get_kind_tree_root(Q) == Q)
struct kind_of_<Q> : std::remove_const_t<decltype(Q)> {
static constexpr auto _quantity_spec_ = Q;
};
@ -456,10 +460,11 @@ struct kind_of_<Q> : std::remove_const_t<decltype(Q)> {
#if MP_UNITS_COMP_CLANG
template<auto Q>
requires detail::QuantitySpecWithNoSpecifiers<std::remove_cvref_t<decltype(Q)>> && (get_kind(Q) == Q)
requires detail::QuantitySpecWithNoSpecifiers<std::remove_cvref_t<decltype(Q)>> &&
(detail::get_kind_tree_root(Q) == Q)
#else
template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(get_kind(Q) == Q)
requires(detail::get_kind_tree_root(Q) == Q)
#endif
struct kind_of_<Q> : quantity_spec<kind_of_<Q>, Q> {
static constexpr auto _quantity_spec_ = Q;
@ -467,7 +472,7 @@ struct kind_of_<Q> : quantity_spec<kind_of_<Q>, Q> {
#endif
template<detail::QuantitySpecWithNoSpecifiers auto Q>
requires(get_kind(Q) == Q)
requires(detail::get_kind_tree_root(Q) == Q)
inline constexpr kind_of_<Q> kind_of;
namespace detail {
@ -484,6 +489,15 @@ template<QuantitySpec auto... From, QuantitySpec Q>
return q;
}
template<QuantitySpec Q>
[[nodiscard]] consteval auto remove_kind(Q q)
{
if constexpr (detail::QuantityKindSpec<Q>)
return Q::_quantity_spec_;
else
return q;
}
} // namespace detail
// Operators
@ -493,7 +507,7 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
{
return detail::clone_kind_of<Lhs{}, Rhs{}>(
detail::expr_multiply<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
detail::remove_kind(lhs), detail::remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
@ -501,7 +515,7 @@ template<QuantitySpec Lhs, QuantitySpec Rhs>
{
return detail::clone_kind_of<Lhs{}, Rhs{}>(
detail::expr_divide<derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(lhs), remove_kind(rhs)));
detail::remove_kind(lhs), detail::remove_kind(rhs)));
}
template<QuantitySpec Lhs, QuantitySpec Rhs>
@ -538,13 +552,13 @@ template<std::intmax_t Num, std::intmax_t Den = 1, QuantitySpec Q>
else if constexpr (detail::IntermediateDerivedQuantitySpec<Q>)
return detail::clone_kind_of<Q{}>(
detail::expr_pow<Num, Den, derived_quantity_spec, struct dimensionless, detail::type_list_of_quantity_spec_less>(
remove_kind(q)));
detail::remove_kind(q)));
else if constexpr (Den == 1)
return detail::clone_kind_of<Q{}>(
derived_quantity_spec<power<std::remove_const_t<decltype(remove_kind(Q{}))>, Num>>{});
derived_quantity_spec<power<std::remove_const_t<decltype(detail::remove_kind(Q{}))>, Num>>{});
else
return detail::clone_kind_of<Q{}>(
derived_quantity_spec<power<std::remove_const_t<decltype(remove_kind(Q{}))>, Num, Den>>{});
derived_quantity_spec<power<std::remove_const_t<decltype(detail::remove_kind(Q{}))>, Num, Den>>{});
}
@ -1315,8 +1329,8 @@ template<QuantitySpec From, QuantitySpec To>
else if constexpr (From{} == To{})
return yes;
else if constexpr (QuantityKindSpec<From> || QuantityKindSpec<To>) {
constexpr auto from_kind = get_kind(From{});
constexpr auto to_kind = get_kind(To{});
constexpr auto from_kind = get_kind_tree_root(From{});
constexpr auto to_kind = get_kind_tree_root(To{});
constexpr auto exploded_kind_result = [](specs_convertible_result res) {
using enum specs_convertible_result;
return res == no ? no : yes;
@ -1327,11 +1341,11 @@ template<QuantitySpec From, QuantitySpec To>
return convertible_impl(from_kind, to_kind);
else if constexpr (get_complexity(from_kind) > get_complexity(to_kind))
return exploded_kind_result(
convertible_impl(get_kind(explode<get_complexity(to_kind)>(from_kind).quantity), to_kind));
convertible_impl(get_kind_tree_root(explode<get_complexity(to_kind)>(from_kind).quantity), to_kind));
else
return exploded_kind_result(
convertible_impl(from_kind, get_kind(explode<get_complexity(from_kind)>(to_kind).quantity)));
} else if constexpr (NestedQuantityKindSpecOf<get_kind(To{}), from> && get_kind(To{}) == To{})
convertible_impl(from_kind, get_kind_tree_root(explode<get_complexity(from_kind)>(to_kind).quantity)));
} else if constexpr (NestedQuantityKindSpecOf<get_kind_tree_root(To{}), from> && get_kind_tree_root(To{}) == To{})
return yes;
else if constexpr (NamedQuantitySpec<From> && NamedQuantitySpec<To>) {
if constexpr (have_common_base(From{}, To{})) {
@ -1401,8 +1415,8 @@ template<QuantitySpec QS1, QuantitySpec QS2>
namespace detail {
template<QuantitySpec Q>
requires requires(Q q) { get_kind(q); }
using to_kind = std::remove_const_t<decltype(get_kind(Q{}))>;
requires requires(Q q) { get_kind_tree_root(q); }
using to_kind = std::remove_const_t<decltype(get_kind_tree_root(Q{}))>;
#ifdef __cpp_explicit_this_parameter
template<NamedQuantitySpec auto QS, auto... Args>
@ -1415,19 +1429,8 @@ template<typename Self, NamedQuantitySpec auto QS, auto... Args>
return contains<struct is_kind, Args...>();
}
} // namespace detail
template<QuantitySpec Q>
[[nodiscard]] consteval auto remove_kind(Q q)
{
if constexpr (detail::QuantityKindSpec<Q>)
return Q::_quantity_spec_;
else
return q;
}
template<QuantitySpec Q>
[[nodiscard]] consteval QuantitySpec auto get_kind(Q q)
[[nodiscard]] consteval QuantitySpec auto get_kind_tree_root(Q q)
{
auto defined_as_kind = []<typename QQ>(QQ qq) {
if constexpr (requires { detail::defined_as_kind(qq); })
@ -1441,7 +1444,7 @@ template<QuantitySpec Q>
} else if constexpr (defined_as_kind(Q{})) {
return q;
} else if constexpr (requires { Q::_parent_; }) {
return get_kind(Q::_parent_);
return get_kind_tree_root(Q::_parent_);
} else if constexpr (detail::IntermediateDerivedQuantitySpec<Q>) {
return detail::expr_map<detail::to_kind, derived_quantity_spec, struct dimensionless,
detail::type_list_of_quantity_spec_less>(q);
@ -1451,20 +1454,29 @@ template<QuantitySpec Q>
}
}
} // namespace detail
template<QuantitySpec Q>
[[nodiscard]] consteval detail::QuantityKindSpec auto get_kind(Q q)
{
return kind_of<detail::get_kind_tree_root(q)>;
}
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(QuantitySpec auto q) { return q; }
template<QuantitySpec Q1, QuantitySpec Q2>
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(Q1 q1, Q2 q2)
requires(implicitly_convertible(get_kind(q1), get_kind(q2)) || implicitly_convertible(get_kind(q2), get_kind(q1)))
requires(implicitly_convertible(get_kind_tree_root(q1), get_kind_tree_root(q2)) ||
implicitly_convertible(get_kind_tree_root(q2), get_kind_tree_root(q1)))
{
using QQ1 = std::remove_const_t<decltype(remove_kind(q1))>;
using QQ2 = std::remove_const_t<decltype(remove_kind(q2))>;
using QQ1 = std::remove_const_t<decltype(detail::remove_kind(q1))>;
using QQ2 = std::remove_const_t<decltype(detail::remove_kind(q2))>;
if constexpr (is_same_v<Q1, Q2>)
return q1;
else if constexpr (detail::NestedQuantityKindSpecOf<Q1{}, Q2{}>)
return remove_kind(q1);
return detail::remove_kind(q1);
else if constexpr (detail::NestedQuantityKindSpecOf<Q2{}, Q1{}>)
return remove_kind(q2);
return detail::remove_kind(q2);
else if constexpr ((detail::QuantityKindSpec<Q1> && !detail::QuantityKindSpec<Q2>) ||
(detail::IntermediateDerivedQuantitySpec<QQ1> && detail::NamedQuantitySpec<QQ2> &&
implicitly_convertible(Q1{}, Q2{})))
@ -1479,10 +1491,10 @@ template<QuantitySpec Q1, QuantitySpec Q2>
return q2;
else if constexpr (implicitly_convertible(Q2{}, Q1{}))
return q1;
else if constexpr (implicitly_convertible(get_kind(Q1{}), get_kind(Q2{})))
return get_kind(q2);
else if constexpr (implicitly_convertible(get_kind_tree_root(Q1{}), get_kind_tree_root(Q2{})))
return get_kind_tree_root(q2);
else
return get_kind(q1);
return get_kind_tree_root(q1);
}
[[nodiscard]] consteval QuantitySpec auto common_quantity_spec(QuantitySpec auto q1, QuantitySpec auto q2,

View File

@ -27,23 +27,23 @@
namespace mp_units::iec80000 {
// clang-format off
template<PrefixableUnit auto U> struct kibi_ : prefixed_unit<"Ki", mag_power<2, 10>, U> {};
template<PrefixableUnit auto U> struct mebi_ : prefixed_unit<"Mi", mag_power<2, 20>, U> {};
template<PrefixableUnit auto U> struct gibi_ : prefixed_unit<"Gi", mag_power<2, 30>, U> {};
template<PrefixableUnit auto U> struct tebi_ : prefixed_unit<"Ti", mag_power<2, 40>, U> {};
template<PrefixableUnit auto U> struct pebi_ : prefixed_unit<"Pi", mag_power<2, 50>, U> {};
template<PrefixableUnit auto U> struct exbi_ : prefixed_unit<"Ei", mag_power<2, 60>, U> {};
template<PrefixableUnit auto U> struct zebi_ : prefixed_unit<"Zi", mag_power<2, 70>, U> {};
template<PrefixableUnit auto U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U> {};
template<PrefixableUnit U> struct kibi_ : prefixed_unit<"Ki", mag_power<2, 10>, U{}> {};
template<PrefixableUnit U> struct mebi_ : prefixed_unit<"Mi", mag_power<2, 20>, U{}> {};
template<PrefixableUnit U> struct gibi_ : prefixed_unit<"Gi", mag_power<2, 30>, U{}> {};
template<PrefixableUnit U> struct tebi_ : prefixed_unit<"Ti", mag_power<2, 40>, U{}> {};
template<PrefixableUnit U> struct pebi_ : prefixed_unit<"Pi", mag_power<2, 50>, U{}> {};
template<PrefixableUnit U> struct exbi_ : prefixed_unit<"Ei", mag_power<2, 60>, U{}> {};
template<PrefixableUnit U> struct zebi_ : prefixed_unit<"Zi", mag_power<2, 70>, U{}> {};
template<PrefixableUnit U> struct yobi_ : prefixed_unit<"Yi", mag_power<2, 80>, U{}> {};
template<PrefixableUnit auto U> inline constexpr kibi_<U> kibi;
template<PrefixableUnit auto U> inline constexpr mebi_<U> mebi;
template<PrefixableUnit auto U> inline constexpr gibi_<U> gibi;
template<PrefixableUnit auto U> inline constexpr tebi_<U> tebi;
template<PrefixableUnit auto U> inline constexpr pebi_<U> pebi;
template<PrefixableUnit auto U> inline constexpr exbi_<U> exbi;
template<PrefixableUnit auto U> inline constexpr zebi_<U> zebi;
template<PrefixableUnit auto U> inline constexpr yobi_<U> yobi;
template<PrefixableUnit auto U> inline constexpr kibi_<std::remove_const_t<decltype(U)>> kibi;
template<PrefixableUnit auto U> inline constexpr mebi_<std::remove_const_t<decltype(U)>> mebi;
template<PrefixableUnit auto U> inline constexpr gibi_<std::remove_const_t<decltype(U)>> gibi;
template<PrefixableUnit auto U> inline constexpr tebi_<std::remove_const_t<decltype(U)>> tebi;
template<PrefixableUnit auto U> inline constexpr pebi_<std::remove_const_t<decltype(U)>> pebi;
template<PrefixableUnit auto U> inline constexpr exbi_<std::remove_const_t<decltype(U)>> exbi;
template<PrefixableUnit auto U> inline constexpr zebi_<std::remove_const_t<decltype(U)>> zebi;
template<PrefixableUnit auto U> inline constexpr yobi_<std::remove_const_t<decltype(U)>> yobi;
// clang-format on
} // namespace mp_units::iec80000

View File

@ -27,55 +27,55 @@
namespace mp_units::si {
// clang-format off
template<PrefixableUnit auto U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U> {};
template<PrefixableUnit auto U> struct ronto_ : prefixed_unit<"r", mag_power<10, -27>, U> {};
template<PrefixableUnit auto U> struct yocto_ : prefixed_unit<"y", mag_power<10, -24>, U> {};
template<PrefixableUnit auto U> struct zepto_ : prefixed_unit<"z", mag_power<10, -21>, U> {};
template<PrefixableUnit auto U> struct atto_ : prefixed_unit<"a", mag_power<10, -18>, U> {};
template<PrefixableUnit auto U> struct femto_ : prefixed_unit<"f", mag_power<10, -15>, U> {};
template<PrefixableUnit auto U> struct pico_ : prefixed_unit<"p", mag_power<10, -12>, U> {};
template<PrefixableUnit auto U> struct nano_ : prefixed_unit<"n", mag_power<10, -9>, U> {};
template<PrefixableUnit auto U> struct micro_ : prefixed_unit<basic_symbol_text{"µ", "u"}, mag_power<10, -6>, U> {};
template<PrefixableUnit auto U> struct milli_ : prefixed_unit<"m", mag_power<10, -3>, U> {};
template<PrefixableUnit auto U> struct centi_ : prefixed_unit<"c", mag_power<10, -2>, U> {};
template<PrefixableUnit auto U> struct deci_ : prefixed_unit<"d", mag_power<10, -1>, U> {};
template<PrefixableUnit auto U> struct deca_ : prefixed_unit<"da", mag_power<10, 1>, U> {};
template<PrefixableUnit auto U> struct hecto_ : prefixed_unit<"h", mag_power<10, 2>, U> {};
template<PrefixableUnit auto U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U> {};
template<PrefixableUnit auto U> struct mega_ : prefixed_unit<"M", mag_power<10, 6>, U> {};
template<PrefixableUnit auto U> struct giga_ : prefixed_unit<"G", mag_power<10, 9>, U> {};
template<PrefixableUnit auto U> struct tera_ : prefixed_unit<"T", mag_power<10, 12>, U> {};
template<PrefixableUnit auto U> struct peta_ : prefixed_unit<"P", mag_power<10, 15>, U> {};
template<PrefixableUnit auto U> struct exa_ : prefixed_unit<"E", mag_power<10, 18>, U> {};
template<PrefixableUnit auto U> struct zetta_ : prefixed_unit<"Z", mag_power<10, 21>, U> {};
template<PrefixableUnit auto U> struct yotta_ : prefixed_unit<"Y", mag_power<10, 24>, U> {};
template<PrefixableUnit auto U> struct ronna_ : prefixed_unit<"R", mag_power<10, 27>, U> {};
template<PrefixableUnit auto U> struct quetta_ : prefixed_unit<"Q", mag_power<10, 30>, U> {};
template<PrefixableUnit U> struct quecto_ : prefixed_unit<"q", mag_power<10, -30>, U{}> {};
template<PrefixableUnit U> struct ronto_ : prefixed_unit<"r", mag_power<10, -27>, U{}> {};
template<PrefixableUnit U> struct yocto_ : prefixed_unit<"y", mag_power<10, -24>, U{}> {};
template<PrefixableUnit U> struct zepto_ : prefixed_unit<"z", mag_power<10, -21>, U{}> {};
template<PrefixableUnit U> struct atto_ : prefixed_unit<"a", mag_power<10, -18>, U{}> {};
template<PrefixableUnit U> struct femto_ : prefixed_unit<"f", mag_power<10, -15>, U{}> {};
template<PrefixableUnit U> struct pico_ : prefixed_unit<"p", mag_power<10, -12>, U{}> {};
template<PrefixableUnit U> struct nano_ : prefixed_unit<"n", mag_power<10, -9>, U{}> {};
template<PrefixableUnit U> struct micro_ : prefixed_unit<basic_symbol_text{"µ", "u"}, mag_power<10, -6>, U{}> {};
template<PrefixableUnit U> struct milli_ : prefixed_unit<"m", mag_power<10, -3>, U{}> {};
template<PrefixableUnit U> struct centi_ : prefixed_unit<"c", mag_power<10, -2>, U{}> {};
template<PrefixableUnit U> struct deci_ : prefixed_unit<"d", mag_power<10, -1>, U{}> {};
template<PrefixableUnit U> struct deca_ : prefixed_unit<"da", mag_power<10, 1>, U{}> {};
template<PrefixableUnit U> struct hecto_ : prefixed_unit<"h", mag_power<10, 2>, U{}> {};
template<PrefixableUnit U> struct kilo_ : prefixed_unit<"k", mag_power<10, 3>, U{}> {};
template<PrefixableUnit U> struct mega_ : prefixed_unit<"M", mag_power<10, 6>, U{}> {};
template<PrefixableUnit U> struct giga_ : prefixed_unit<"G", mag_power<10, 9>, U{}> {};
template<PrefixableUnit U> struct tera_ : prefixed_unit<"T", mag_power<10, 12>, U{}> {};
template<PrefixableUnit U> struct peta_ : prefixed_unit<"P", mag_power<10, 15>, U{}> {};
template<PrefixableUnit U> struct exa_ : prefixed_unit<"E", mag_power<10, 18>, U{}> {};
template<PrefixableUnit U> struct zetta_ : prefixed_unit<"Z", mag_power<10, 21>, U{}> {};
template<PrefixableUnit U> struct yotta_ : prefixed_unit<"Y", mag_power<10, 24>, U{}> {};
template<PrefixableUnit U> struct ronna_ : prefixed_unit<"R", mag_power<10, 27>, U{}> {};
template<PrefixableUnit U> struct quetta_ : prefixed_unit<"Q", mag_power<10, 30>, U{}> {};
template<PrefixableUnit auto U> inline constexpr quecto_<U> quecto;
template<PrefixableUnit auto U> inline constexpr ronto_<U> ronto;
template<PrefixableUnit auto U> inline constexpr yocto_<U> yocto;
template<PrefixableUnit auto U> inline constexpr zepto_<U> zepto;
template<PrefixableUnit auto U> inline constexpr atto_<U> atto;
template<PrefixableUnit auto U> inline constexpr femto_<U> femto;
template<PrefixableUnit auto U> inline constexpr pico_<U> pico;
template<PrefixableUnit auto U> inline constexpr nano_<U> nano;
template<PrefixableUnit auto U> inline constexpr micro_<U> micro;
template<PrefixableUnit auto U> inline constexpr milli_<U> milli;
template<PrefixableUnit auto U> inline constexpr centi_<U> centi;
template<PrefixableUnit auto U> inline constexpr deci_<U> deci;
template<PrefixableUnit auto U> inline constexpr deca_<U> deca;
template<PrefixableUnit auto U> inline constexpr hecto_<U> hecto;
template<PrefixableUnit auto U> inline constexpr kilo_<U> kilo;
template<PrefixableUnit auto U> inline constexpr mega_<U> mega;
template<PrefixableUnit auto U> inline constexpr giga_<U> giga;
template<PrefixableUnit auto U> inline constexpr tera_<U> tera;
template<PrefixableUnit auto U> inline constexpr peta_<U> peta;
template<PrefixableUnit auto U> inline constexpr exa_<U> exa;
template<PrefixableUnit auto U> inline constexpr zetta_<U> zetta;
template<PrefixableUnit auto U> inline constexpr yotta_<U> yotta;
template<PrefixableUnit auto U> inline constexpr ronna_<U> ronna;
template<PrefixableUnit auto U> inline constexpr quetta_<U> quetta;
template<PrefixableUnit auto U> inline constexpr quecto_<std::remove_const_t<decltype(U)>> quecto;
template<PrefixableUnit auto U> inline constexpr ronto_<std::remove_const_t<decltype(U)>> ronto;
template<PrefixableUnit auto U> inline constexpr yocto_<std::remove_const_t<decltype(U)>> yocto;
template<PrefixableUnit auto U> inline constexpr zepto_<std::remove_const_t<decltype(U)>> zepto;
template<PrefixableUnit auto U> inline constexpr atto_<std::remove_const_t<decltype(U)>> atto;
template<PrefixableUnit auto U> inline constexpr femto_<std::remove_const_t<decltype(U)>> femto;
template<PrefixableUnit auto U> inline constexpr pico_<std::remove_const_t<decltype(U)>> pico;
template<PrefixableUnit auto U> inline constexpr nano_<std::remove_const_t<decltype(U)>> nano;
template<PrefixableUnit auto U> inline constexpr micro_<std::remove_const_t<decltype(U)>> micro;
template<PrefixableUnit auto U> inline constexpr milli_<std::remove_const_t<decltype(U)>> milli;
template<PrefixableUnit auto U> inline constexpr centi_<std::remove_const_t<decltype(U)>> centi;
template<PrefixableUnit auto U> inline constexpr deci_<std::remove_const_t<decltype(U)>> deci;
template<PrefixableUnit auto U> inline constexpr deca_<std::remove_const_t<decltype(U)>> deca;
template<PrefixableUnit auto U> inline constexpr hecto_<std::remove_const_t<decltype(U)>> hecto;
template<PrefixableUnit auto U> inline constexpr kilo_<std::remove_const_t<decltype(U)>> kilo;
template<PrefixableUnit auto U> inline constexpr mega_<std::remove_const_t<decltype(U)>> mega;
template<PrefixableUnit auto U> inline constexpr giga_<std::remove_const_t<decltype(U)>> giga;
template<PrefixableUnit auto U> inline constexpr tera_<std::remove_const_t<decltype(U)>> tera;
template<PrefixableUnit auto U> inline constexpr peta_<std::remove_const_t<decltype(U)>> peta;
template<PrefixableUnit auto U> inline constexpr exa_<std::remove_const_t<decltype(U)>> exa;
template<PrefixableUnit auto U> inline constexpr zetta_<std::remove_const_t<decltype(U)>> zetta;
template<PrefixableUnit auto U> inline constexpr yotta_<std::remove_const_t<decltype(U)>> yotta;
template<PrefixableUnit auto U> inline constexpr ronna_<std::remove_const_t<decltype(U)>> ronna;
template<PrefixableUnit auto U> inline constexpr quetta_<std::remove_const_t<decltype(U)>> quetta;
// clang-format on
} // namespace mp_units::si

View File

@ -24,7 +24,7 @@ cmake_minimum_required(VERSION 3.5)
find_package(Catch2 3 REQUIRED)
add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp)
add_executable(unit_tests_runtime distribution_test.cpp fmt_test.cpp math_test.cpp atomic_test.cpp)
if(${projectPrefix}BUILD_CXX_MODULES)
target_compile_definitions(unit_tests_runtime PUBLIC ${projectPrefix}MODULES)
endif()

View File

@ -0,0 +1,37 @@
// The MIT License (MIT)
//
// Copyright (c) 2024 Nick Thompson
//
// 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.
#include <catch2/catch_all.hpp>
#include <mp-units/systems/isq/space_and_time.h>
#include <mp-units/systems/si/unit_symbols.h>
#include <mp-units/systems/si/units.h>
#include <atomic>
using namespace mp_units;
using namespace mp_units::si::unit_symbols;
TEST_CASE("std::atomic works with dimensioned types", "[atomic][assignment]")
{
std::atomic<quantity<isq::area[m2]>> a1 = 3.0 * isq::area[m2];
std::atomic<quantity<isq::area[m2]>> a2 = 3.0 * isq::area[m2];
REQUIRE(a1.load() == a2.load());
}

View File

@ -396,25 +396,25 @@ static_assert(acceleration * (time * time) != position_vector);
static_assert(acceleration / speed != frequency);
// get_kind
static_assert(get_kind(length) == length);
static_assert(get_kind(distance) == length);
static_assert(get_kind(time) == time);
static_assert(get_kind(period_duration) == time);
static_assert(get_kind(length / time) == length / time);
static_assert(get_kind(speed) == speed);
static_assert(get_kind(height / time) == length / time);
static_assert(get_kind(inverse(time)) == inverse(time));
static_assert(get_kind(inverse(period_duration)) == inverse(time));
static_assert(get_kind(frequency) == frequency);
static_assert(get_kind(mass * frequency) == mass * frequency);
static_assert(get_kind(moment_of_force) == moment_of_force);
static_assert(get_kind(energy) == energy);
static_assert(get_kind(potential_energy) == energy);
static_assert(get_kind(kinetic_energy) == energy);
static_assert(get_kind(pow<1, 2>(area)) == pow<1, 2>(area));
static_assert(get_kind(angular_measure) == angular_measure);
static_assert(get_kind(phase_angle) == angular_measure);
static_assert(get_kind(rotational_displacement) == angular_measure);
static_assert(get_kind(length) == kind_of<length>);
static_assert(get_kind(distance) == kind_of<length>);
static_assert(get_kind(time) == kind_of<time>);
static_assert(get_kind(period_duration) == kind_of<time>);
static_assert(get_kind(length / time) == kind_of<length / time>);
static_assert(get_kind(speed) == kind_of<speed>);
static_assert(get_kind(height / time) == kind_of<length / time>);
static_assert(get_kind(inverse(time)) == kind_of<inverse(time)>);
static_assert(get_kind(inverse(period_duration)) == kind_of<inverse(time)>);
static_assert(get_kind(frequency) == kind_of<frequency>);
static_assert(get_kind(mass * frequency) == kind_of<mass * frequency>);
static_assert(get_kind(moment_of_force) == kind_of<moment_of_force>);
static_assert(get_kind(energy) == kind_of<energy>);
static_assert(get_kind(potential_energy) == kind_of<energy>);
static_assert(get_kind(kinetic_energy) == kind_of<energy>);
static_assert(get_kind(pow<1, 2>(area)) == kind_of<pow<1, 2>(area)>);
static_assert(get_kind(angular_measure) == kind_of<angular_measure>);
static_assert(get_kind(phase_angle) == kind_of<angular_measure>);
static_assert(get_kind(rotational_displacement) == kind_of<angular_measure>);
// comparisons of the same dimensions
static_assert(length == length);
@ -507,7 +507,7 @@ static_assert(convertible_impl(width, width) == yes);
static_assert(convertible_impl(energy, energy) == yes);
static_assert(convertible_impl(kind_of<length>, kind_of<length>) == yes);
static_assert(convertible_impl(kind_of<energy>, kind_of<energy>) == yes);
static_assert(convertible_impl(kind_of<get_kind(moment_of_force)>, kind_of<get_kind(moment_of_force)>) == yes);
static_assert(convertible_impl(get_kind(moment_of_force), get_kind(moment_of_force)) == yes);
// converting to a different branch
static_assert(convertible_impl(height, width) == cast);

View File

@ -520,7 +520,8 @@ static_assert(is_of_type<1 * percent * (1 * m), quantity<derived_unit<struct per
static_assert(is_of_type<1 * m / (1 * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, int>>);
static_assert(is_of_type<1 * m / (1 * m), quantity<one, int>>);
static_assert(is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<si::metre>, per<struct si::metre>>{}, int>>);
static_assert(
is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, int>>);
static_assert(is_of_type<1 * m / 1, quantity<si::metre, int>>);
static_assert(is_of_type<1 * m / (1 * one), quantity<si::metre, int>>);
@ -568,7 +569,7 @@ static_assert(is_of_type<1 * percent * (1. * m), quantity<derived_unit<struct pe
static_assert(is_of_type<1 * m / (1. * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, double>>);
static_assert(is_of_type<1. * m / (1 * m), quantity<one, double>>);
static_assert(
is_of_type<1. * km / (1 * m), quantity<derived_unit<si::kilo_<si::metre>, per<struct si::metre>>{}, double>>);
is_of_type<1. * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, double>>);
static_assert(is_of_type<1. * m / 1, quantity<si::metre, double>>);
static_assert(is_of_type<1 * m / (1. * one), quantity<si::metre, double>>);
@ -612,11 +613,12 @@ static_assert(is_of_type<1 * m * (1 * min), quantity<derived_unit<struct si::met
static_assert(is_of_type<1 * s * (1 * Hz), quantity<derived_unit<struct si::hertz, struct si::second>{}, int>>);
static_assert(is_of_type<1 / (1 * min), quantity<derived_unit<struct one, per<struct si::minute>>{}, int>>);
static_assert(is_of_type<1 / (1 * Hz), quantity<derived_unit<struct one, per<struct si::hertz>>{}, int>>);
static_assert(is_of_type<1 / (1 * km), quantity<derived_unit<struct one, per<si::kilo_<si::metre>>>{}, int>>);
static_assert(is_of_type<1 / (1 * km), quantity<derived_unit<struct one, per<si::kilo_<struct si::metre>>>{}, int>>);
static_assert(is_of_type<1 / min, quantity<derived_unit<struct one, per<struct si::minute>>{}, int>>);
static_assert(is_of_type<1 / Hz, quantity<derived_unit<struct one, per<struct si::hertz>>{}, int>>);
static_assert(is_of_type<1 / km, quantity<derived_unit<struct one, per<si::kilo_<si::metre>>>{}, int>>);
static_assert(is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<si::metre>, per<struct si::metre>>{}, int>>);
static_assert(is_of_type<1 / km, quantity<derived_unit<struct one, per<si::kilo_<struct si::metre>>>{}, int>>);
static_assert(
is_of_type<1 * km / (1 * m), quantity<derived_unit<si::kilo_<struct si::metre>, per<struct si::metre>>{}, int>>);
static_assert(is_of_type<1 * m / (1 * s), quantity<derived_unit<struct si::metre, per<struct si::second>>{}, int>>);
static_assert(is_of_type<1 * m / (1 * min), quantity<derived_unit<struct si::metre, per<struct si::minute>>{}, int>>);
static_assert(is_of_type<1 * min / (1 * m), quantity<derived_unit<struct si::minute, per<struct si::metre>>{}, int>>);

View File

@ -61,8 +61,9 @@ static_assert(1 * Rm == 1'000'000'000'000'000'000 * Gm);
static_assert(1 * Qm == 1'000'000'000'000'000'000 * Tm);
// check for invalid prefixes
template<template<auto U> typename prefix, auto V1>
concept can_not_be_prefixed = Unit<std::remove_const_t<decltype(V1)>> && !requires { typename prefix<V1>; };
template<template<typename U> typename prefix, auto V1>
concept can_not_be_prefixed =
Unit<std::remove_const_t<decltype(V1)>> && !requires { typename prefix<std::remove_const_t<decltype(V1)>>; };
static_assert(can_not_be_prefixed<si::milli_, si::degree_Celsius>);
static_assert(can_not_be_prefixed<si::milli_, si::minute>);

View File

@ -212,8 +212,8 @@ static_assert(convertible(kilojoule, joule));
static_assert(kilojoule != joule);
static_assert(kilojoule.symbol == "kJ");
static_assert(is_of_type<si::kilo<metre>, si::kilo_<metre>>);
static_assert(is_of_type<si::kilo<joule>, si::kilo_<joule>>);
static_assert(is_of_type<si::kilo<metre>, si::kilo_<metre_>>);
static_assert(is_of_type<si::kilo<joule>, si::kilo_<joule_>>);
// TODO Should the below be a scaled version of metre^2?
static_assert(is_of_type<kilometre * metre, derived_unit<kilometre_, metre_>>); // !!!
@ -264,7 +264,7 @@ static_assert(is_of_type<get_canonical_unit(km_2).reference_unit, metre_>);
static_assert(get_canonical_unit(km_2).mag == mag<2000>);
constexpr auto kJ_42 = mag<42> * si::kilo<joule>;
static_assert(is_of_type<kJ_42, scaled_unit<mag<42>, si::kilo_<joule>>>);
static_assert(is_of_type<kJ_42, scaled_unit<mag<42>, si::kilo_<joule_>>>);
static_assert(
is_of_type<get_canonical_unit(kJ_42).reference_unit, derived_unit<gram_, power<metre_, 2>, per<power<second_, 2>>>>);
static_assert(get_canonical_unit(kJ_42).mag == mag<42'000'000>);
@ -467,7 +467,7 @@ static_assert(!convertible(metre, metre* metre));
// one
static_assert(is_of_type<metre / metre, one_>);
static_assert(is_of_type<si::kilo<metre> / metre, derived_unit<si::kilo_<metre>, per<metre_>>>);
static_assert(is_of_type<si::kilo<metre> / metre, derived_unit<si::kilo_<metre_>, per<metre_>>>);
static_assert(metre / metre == one);
static_assert(hertz * second == one);
static_assert(one * one == one);
@ -509,7 +509,7 @@ static_assert(is_of_type<pow<1, 2>(metre / (second * second)), derived_unit<powe
static_assert(is_of_type<kilometre * kilometre, derived_unit<power<kilometre_, 2>>>);
static_assert(is_of_type<pow<2>(kilometre), derived_unit<power<kilometre_, 2>>>);
static_assert(is_of_type<pow<2>(si::kilo<metre>), derived_unit<power<si::kilo_<metre>, 2>>>);
static_assert(is_of_type<pow<2>(si::kilo<metre>), derived_unit<power<si::kilo_<metre_>, 2>>>);
static_assert(is_of_type<pow<2>(hour), derived_unit<power<hour_, 2>>>);
static_assert(
is_of_type<pow<2>(mag<3600>* second), scaled_unit<mag<3600> * mag<3600>, derived_unit<power<second_, 2>>>>);