Skip to content

Commit

Permalink
feat: 💥 unit_can_be_prefixed removed - from now on all named units …
Browse files Browse the repository at this point in the history
…can be prefixed

Resolves #604
  • Loading branch information
mpusz committed Aug 22, 2024
1 parent aab0ef8 commit 77625d6
Show file tree
Hide file tree
Showing 7 changed files with 15 additions and 108 deletions.
16 changes: 2 additions & 14 deletions docs/users_guide/framework_basics/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,8 @@ and is satisfied by:

### `PrefixableUnit<T>` { #PrefixableUnit }

`PrefixableUnit` concept is satisfied by all units derived from a `named_unit` class template for
which a customization point `unit_can_be_prefixed<T{}>` was not explicitly set to `false`. Such
units can be passed as an argument to a `prefixed_unit` class template.

??? abstract "Examples"

All units in the [SI](../../appendix/glossary.md#si) can be prefixed with SI-defined prefixes.

Some [off-system units](../../appendix/glossary.md#off-system-unit) like `non_si::day`
can't be prefixed. To enforce that, the following has to be provided:

```cpp
template<> inline constexpr bool unit_can_be_prefixed<non_si::day> = false;
```
`PrefixableUnit` concept is satisfied by all units derived from a `named_unit` class template.
Such units can be passed as an argument to a `prefixed_unit` class template.


### `UnitOf<T, V>` { #UnitOf }
Expand Down
18 changes: 0 additions & 18 deletions docs/users_guide/framework_basics/systems_of_units.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,21 +190,3 @@ inline constexpr struct mag_pi final : magnitude<std::numbers::pi_v<long double>
```cpp
inline constexpr struct degree final : named_unit<{u8"°", "deg"}, mag_pi / mag<180> * si::radian> {} degree;
```
!!! tip
The ISO 8000 and [SI](../../appendix/glossary.md#si) standards explicitly forbid using prefixes
with some units (e.g., day, minute, hour, degree Celsius). This is why the library disallows
this as well by providing specializations of the `unit_can_be_prefixed` variable template for
such units. Thanks to it trying to create a prefixed version for them will result in
a compile-time error.
However, some projects are not aware of those limitations and use prefixed version of such units
(e.g., [linux kernel uses millidegrees Celsius](https://github.com/search?q=repo%3Atorvalds%2Flinux+millidegree&type=code)).
To enable compatibility with those projects we can workaround the limitation in **mp-units**
by providing a scaled version of the unit explicitly:
```cpp
inline constexpr struct milli_degree_celsius final : named_unit<symbol_text{u8"m℃", "m`C"}, mag_ratio<1, 1000> * si::degree_Celsius> {} milli_degree_celsius;
inline constexpr auto mdeg_C = milli_degree_celsius;
```
27 changes: 1 addition & 26 deletions src/core/include/mp-units/framework/unit_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,38 +62,13 @@ template<typename T>
inline constexpr bool is_derived_from_specialization_of_named_unit =
requires(T* t) { to_base_specialization_of_named_unit(t); };

template<typename T>
inline constexpr bool is_specialization_of_named_unit = false;

template<symbol_text Symbol, auto... Args>
inline constexpr bool is_specialization_of_named_unit<named_unit<Symbol, Args...>> = true;

/**
* @brief A concept matching all units with special names
*
* Satisfied by all unit types derived from the specialization of `named_unit`.
*/
template<typename T>
concept NamedUnit =
Unit<T> && detail::is_derived_from_specialization_of_named_unit<T> && (!detail::is_specialization_of_named_unit<T>);

} // namespace detail

/**
* @brief Prevents assignment of a prefix to specific units
*
* By default all named units allow assigning a prefix for them. There are some notable exceptions like
* `hour` or `degree_Celsius`. For those a partial specialization with the value `false` should be
* provided.
*/
MP_UNITS_EXPORT template<Unit auto V>
inline constexpr bool unit_can_be_prefixed = true;

/**
* @brief A concept to be used to define prefixes for a unit
*/
MP_UNITS_EXPORT template<typename T>
concept PrefixableUnit = detail::NamedUnit<T> && unit_can_be_prefixed<T{}>;
concept PrefixableUnit = Unit<T> && detail::is_derived_from_specialization_of_named_unit<T>;

namespace detail {

Expand Down
9 changes: 0 additions & 9 deletions src/systems/include/mp-units/systems/si/units.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,6 @@ using namespace non_si;

} // namespace si

template<>
inline constexpr bool unit_can_be_prefixed<si::degree_Celsius> = false;
template<>
inline constexpr bool unit_can_be_prefixed<non_si::minute> = false;
template<>
inline constexpr bool unit_can_be_prefixed<non_si::hour> = false;
template<>
inline constexpr bool unit_can_be_prefixed<non_si::day> = false;

template<>
inline constexpr bool space_before_unit_symbol<non_si::degree> = false;
template<>
Expand Down
25 changes: 0 additions & 25 deletions test/static/concepts_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,31 +155,6 @@ static_assert(!Unit<int>);
static_assert(!Unit<std::chrono::seconds>);
#endif

// NamedUnit
static_assert(detail::NamedUnit<struct si::metre>);
static_assert(detail::NamedUnit<struct natural::electronvolt>);
static_assert(!detail::NamedUnit<decltype(si::kilogram)>);
static_assert(!detail::NamedUnit<decltype(si::kilo<si::gram>)>);
static_assert(!detail::NamedUnit<decltype(si::metre / si::second)>);
static_assert(!detail::NamedUnit<decltype(inverse(si::second))>);
static_assert(!detail::NamedUnit<decltype(mag<10> * si::second)>);
static_assert(!detail::NamedUnit<decltype(square(si::metre))>);
static_assert(!detail::NamedUnit<decltype(pow<2>(si::metre))>);
static_assert(detail::NamedUnit<struct si::standard_gravity>);
static_assert(!detail::NamedUnit<scaled_unit<mag<10>, struct si::second>>);
static_assert(!detail::NamedUnit<derived_unit<struct si::metre, per<struct si::second>>>);
static_assert(!detail::NamedUnit<struct one>);
static_assert(!detail::NamedUnit<named_unit<"?", kind_of<isq::length>>>);
static_assert(!detail::NamedUnit<named_unit<"?">>);
static_assert(!detail::NamedUnit<named_unit<"?", si::metre / si::second>>);
static_assert(!detail::NamedUnit<named_unit<"?", si::metre, kind_of<isq::length>>>);
static_assert(!detail::NamedUnit<prefixed_unit<"?", mag<10>, si::second>>);
static_assert(!detail::NamedUnit<struct isq::dim_length>);
static_assert(!detail::NamedUnit<int>);
#if MP_UNITS_HOSTED
static_assert(!detail::NamedUnit<std::chrono::seconds>);
#endif

// PrefixableUnit
static_assert(PrefixableUnit<struct si::metre>);
static_assert(PrefixableUnit<struct natural::electronvolt>);
Expand Down
4 changes: 0 additions & 4 deletions test/static/si_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@ static_assert(1 * Qm == 1'000'000'000'000'000'000 * Tm);
template<template<typename U> typename prefix, auto V1>
concept can_not_be_prefixed = Unit<decltype(V1)> && !requires { typename prefix<decltype(V1)>; };

static_assert(can_not_be_prefixed<si::milli_, si::degree_Celsius>);
static_assert(can_not_be_prefixed<si::milli_, si::minute>);
static_assert(can_not_be_prefixed<si::milli_, si::hour>);
static_assert(can_not_be_prefixed<si::milli_, si::day>);
static_assert(can_not_be_prefixed<si::milli_, si::kilogram>);
static_assert(can_not_be_prefixed<si::milli_, si::hectare>);
static_assert(can_not_be_prefixed<si::milli_, si::kilo<si::metre>>);
Expand Down
24 changes: 12 additions & 12 deletions test/static/unit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,18 @@ static_assert(Unit<decltype(metre / second)>);
static_assert(Unit<decltype(nu_second / nu_second)>);
static_assert(Unit<decltype(kilometre)>);

static_assert(detail::NamedUnit<metre_>);
static_assert(detail::NamedUnit<hertz_>);
static_assert(detail::NamedUnit<newton_>);
static_assert(detail::NamedUnit<minute_>);
static_assert(detail::NamedUnit<radian_>);
static_assert(!detail::NamedUnit<decltype(kilogram)>);
static_assert(!detail::NamedUnit<decltype(kilojoule)>);
static_assert(!detail::NamedUnit<decltype(si::kilo<gram>)>);
static_assert(!detail::NamedUnit<decltype(square(metre))>);
static_assert(!detail::NamedUnit<decltype(cubic(metre))>);
static_assert(!detail::NamedUnit<decltype(mag<60> * second)>);
static_assert(!detail::NamedUnit<decltype(kilometre)>);
static_assert(PrefixableUnit<metre_>);
static_assert(PrefixableUnit<hertz_>);
static_assert(PrefixableUnit<newton_>);
static_assert(PrefixableUnit<minute_>);
static_assert(PrefixableUnit<radian_>);
static_assert(!PrefixableUnit<decltype(kilogram)>);
static_assert(!PrefixableUnit<decltype(kilojoule)>);
static_assert(!PrefixableUnit<decltype(si::kilo<gram>)>);
static_assert(!PrefixableUnit<decltype(square(metre))>);
static_assert(!PrefixableUnit<decltype(cubic(metre))>);
static_assert(!PrefixableUnit<decltype(mag<60> * second)>);
static_assert(!PrefixableUnit<decltype(kilometre)>);

// named unit
static_assert(is_of_type<metre, metre_>);
Expand Down

0 comments on commit 77625d6

Please sign in to comment.