Skip to content

Commit

Permalink
Merge branch 'quantity_spec_refactor'
Browse files Browse the repository at this point in the history
  • Loading branch information
mpusz committed Dec 28, 2024
2 parents b685521 + 5382e08 commit 74988f6
Show file tree
Hide file tree
Showing 30 changed files with 1,018 additions and 1,137 deletions.
12 changes: 6 additions & 6 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
default_stages: [commit]
default_stages: [pre-commit]

repos:
- repo: meta
hooks:
- id: check-hooks-apply
- id: check-useless-excludes
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v19.1.2
rev: v19.1.5
hooks:
- id: clang-format
- repo: https://github.com/cheshirekow/cmake-format-precommit
Expand All @@ -23,17 +23,17 @@ repos:
# additional_dependencies: ["cmakelang"]
# exclude: "cmake/.*"
- repo: https://github.com/psf/black
rev: 22.10.0
rev: 24.10.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
args: [--profile, black, --multi-line, "3"]
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
rev: 7.1.1
hooks:
- id: flake8

Expand Down
12 changes: 6 additions & 6 deletions conanfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,9 @@ def generate(self):
tc.absolute_paths = True # only needed for CMake CI
if self._build_all:
tc.cache_variables["CMAKE_EXPORT_COMPILE_COMMANDS"] = True
tc.cache_variables[
"CMAKE_VERIFY_INTERFACE_HEADER_SETS"
] = not opt.import_std
tc.cache_variables["CMAKE_VERIFY_INTERFACE_HEADER_SETS"] = (
not opt.import_std
)
tc.cache_variables["MP_UNITS_DEV_BUILD_LA"] = not self._skip_la
if self._run_clang_tidy:
tc.cache_variables["MP_UNITS_DEV_CLANG_TIDY"] = True
Expand All @@ -264,9 +264,9 @@ def generate(self):
if opt.import_std:
tc.cache_variables["CMAKE_CXX_MODULE_STD"] = True
# Current experimental support according to `Help/dev/experimental.rst`
tc.cache_variables[
"CMAKE_EXPERIMENTAL_CXX_IMPORT_STD"
] = "0e5b6991-d74f-4b3d-a41c-cf096e0b2508"
tc.cache_variables["CMAKE_EXPERIMENTAL_CXX_IMPORT_STD"] = (
"0e5b6991-d74f-4b3d-a41c-cf096e0b2508"
)

# TODO remove the below when Conan will learn to handle C++ modules
if opt.freestanding:
Expand Down
43 changes: 8 additions & 35 deletions docs/users_guide/framework_basics/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,6 @@ All of the above quantity specifications have to be marked as `final`.
`QuantitySpecOf` concept is satisfied when both arguments satisfy a [`QuantitySpec`](#QuantitySpec) concept
and when `T` is implicitly convertible to `V`.

??? info "More details"

Additionally:

- `T` should not be a [nested quantity specification of `V`](dimensionless_quantities.md/#nested-quantity-kinds)
- either `T` is quantity kind or `V` should not be a
[nested quantity specification of `T`](dimensionless_quantities.md/#nested-quantity-kinds)

Those additional conditions are required to make the following work:

```cpp
static_assert(ReferenceOf<si::radian, isq::angular_measure>);
static_assert(!ReferenceOf<si::radian, dimensionless>);
static_assert(!ReferenceOf<isq::angular_measure[si::radian], dimensionless>);
static_assert(ReferenceOf<one, isq::angular_measure>);
static_assert(!ReferenceOf<dimensionless[one], isq::angular_measure>);
```


## `Unit<T>` { #Unit }

Expand Down Expand Up @@ -123,17 +105,8 @@ Such units can be passed as an argument to a `prefixed_unit` class template.

### `UnitOf<T, V>` { #UnitOf }

`UnitOf` concept is satisfied for all units `T` matching an [`AssociatedUnit`](#AssociatedUnit)
concept with an associated quantity type implicitly convertible to `V`.

??? info "More details"

Additionally, the kind of `V` and the kind of quantity type associated with `T` must be the same,
or the quantity type associated with `T` may not be derived from the kind of `V`.

This condition is required to make `dimensionless[si::radian]` invalid as `si::radian` should
be only used for `isq::angular_measure`, which is a
[nested quantity kind within the dimensionless quantities tree](dimensionless_quantities.md/#nested-quantity-kinds).
`UnitOf` concept is satisfied for all units `T` for which an associated quantity spec is implicitly
convertible to the provided [`QuantitySpec`](#QuantitySpec) value.


## `Reference<T>` { #Reference }
Expand All @@ -150,7 +123,7 @@ A `Reference` can either be:
### `ReferenceOf<T, V>` { #ReferenceOf }

`ReferenceOf` concept is satisfied by references `T` which have a quantity specification that satisfies
[`QuantitySpecOf<V>`](#QuantitySpecOf) concept. |
[`QuantitySpecOf<V>`](#QuantitySpecOf) concept.


## `Representation<T>` { #Representation }
Expand Down Expand Up @@ -205,7 +178,7 @@ satisfied by all types being or deriving from an instantiation of a `quantity` c

### `QuantityOf<T, V>` { #QuantityOf }

`QuantityOf` concept is satisfied by all the quantities for which a [`QuantitySpecOf<V>`](#QuantitySpecOf)
`QuantityOf` concept is satisfied by all the quantities for which a [`ReferenceOf<V>`](#ReferenceOf)
is `true`.


Expand Down Expand Up @@ -252,10 +225,10 @@ class template.

`QuantityPointOf` concept is satisfied by all the quantity points `T` that match the following value `V`:

| `V` | Condition |
|----------------|-----------------------------------------------------------------------------------------------------|
| `QuantitySpec` | The quantity point quantity specification satisfies [`QuantitySpecOf<V>`](#QuantitySpecOf) concept. |
| `PointOrigin` | The _point_ and `V` have the same absolute point origin. |
| `V` | Condition |
|----------------|-----------------------------------------------------------------------------------------------|
| `QuantitySpec` | The quantity point quantity specification satisfies [`ReferenceOf<V>`](#ReferenceOf) concept. |
| `PointOrigin` | The _point_ and `V` have the same absolute point origin. |


## `QuantityLike<T>` { #QuantityLike }
Expand Down
21 changes: 21 additions & 0 deletions docs/users_guide/framework_basics/dimensionless_quantities.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,3 +311,24 @@ inline constexpr struct bit final : named_unit<"bit", one, kind_of<storage_capac
```
but still allow the usage of `one` and its scaled versions for such quantities.
!!! info
It is worth mentioning here that converting up the hierarchy beyond a subkind requires an
explicit conversion. For example:
```cpp
static_assert(implicitly_convertible(isq::rotation, dimensionless));
static_assert(!implicitly_convertible(isq::angular_measure, dimensionless));
static_assert(explicitly_convertible(isq::angular_measure, dimensionless));
```
This increases type safety and prevents accidental quantities with invalid units. For example,
a result of a conversion from `isq::angular_measure[rad]` to `dimensionless` would be
a reference of `dimensionless[rad]`, which contains an incorrect unit for a `dimensionless`
quantity. Such a conversion must be explicit and be preceded by an explicit unit conversion:
```cpp
quantity q1 = isq::angular_measure(42. * rad);
quantity<dimensionless[one]> q2 = dimensionless(q1.in(one));
```
4 changes: 2 additions & 2 deletions example/include/geographic.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,14 +193,14 @@ distance spherical_distance(position<T> from, position<T> to)
// const auto central_angle = 2 * asin(sqrt(0.5 - cos(to_lat - from_lat) / 2 + cos(from_lat) * cos(to_lat) * (1
// - cos(lon2_rad - from_lon)) / 2));

return quantity_cast<isq::distance>(earth_radius * central_angle);
return quantity_cast<isq::distance>((earth_radius * central_angle).in(earth_radius.unit));
} else {
// the haversine formula
const quantity sin_lat = sin((to_lat - from_lat) / 2);
const quantity sin_lon = sin((to_lon - from_lon) / 2);
const quantity central_angle = 2 * asin(sqrt(sin_lat * sin_lat + cos(from_lat) * cos(to_lat) * sin_lon * sin_lon));

return quantity_cast<isq::distance>(earth_radius * central_angle);
return quantity_cast<isq::distance>((earth_radius * central_angle).in(earth_radius.unit));
}
}

Expand Down
40 changes: 24 additions & 16 deletions src/core/include/mp-units/bits/quantity_spec_hierarchy.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,16 @@ template<QuantitySpec A, QuantitySpec B>
template<QuantitySpec A, QuantitySpec B>
[[nodiscard]] consteval bool have_common_base(A a, B b)
{
constexpr std::size_t a_length = hierarchy_path_length(A{});
constexpr std::size_t b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return have_common_base_in_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
if constexpr (is_same_v<A, B>)
return true;
else {
constexpr std::size_t a_length = hierarchy_path_length(A{});
constexpr std::size_t b_length = hierarchy_path_length(B{});
if constexpr (a_length > b_length)
return have_common_base_in_hierarchy_of_equal_length(hierarchy_path_advance<a_length - b_length>(a), b);
else
return have_common_base_in_hierarchy_of_equal_length(a, hierarchy_path_advance<b_length - a_length>(b));
}
}

template<QuantitySpec A, QuantitySpec B>
Expand Down Expand Up @@ -91,16 +95,20 @@ template<QuantitySpec A, QuantitySpec B>
template<QuantitySpec Child, QuantitySpec Parent>
[[nodiscard]] consteval bool is_child_of(Child ch, Parent p)
{
if constexpr (Child{} == Parent{})
return std::true_type{};
else {
constexpr std::size_t child_length = hierarchy_path_length(Child{});
constexpr std::size_t parent_length = hierarchy_path_length(Parent{});
if constexpr (parent_length > child_length)
return false;
else
return hierarchy_path_advance<child_length - parent_length>(ch) == p;
}
constexpr std::size_t child_length = hierarchy_path_length(Child{});
constexpr std::size_t parent_length = hierarchy_path_length(Parent{});
if constexpr (parent_length >= child_length)
return false;
else
return hierarchy_path_advance<child_length - parent_length>(ch) == p;
}

[[nodiscard]] consteval QuantitySpec auto get_hierarchy_root(QuantitySpec auto q)
{
if constexpr (requires { q._parent_; })
return get_hierarchy_root(q._parent_);
else
return q;
}

} // namespace mp_units::detail
16 changes: 16 additions & 0 deletions src/core/include/mp-units/bits/type_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,22 @@ struct type_list_split_half;
template<template<typename...> typename List, typename... Types>
struct type_list_split_half<List<Types...>> : type_list_split<List<Types...>, (sizeof...(Types) + 1) / 2> {};

// extract
template<TypeList, TypeList>
struct type_list_extract_impl;

template<template<typename...> typename List, typename... Pre, typename Element, typename... Post>
struct type_list_extract_impl<List<Pre...>, List<Element, Post...>> {
using element = Element;
using rest = List<Pre..., Post...>;
};

template<TypeList List, std::size_t N>
requires(N >= 0) && (type_list_size<List> > N)
struct type_list_extract :
type_list_extract_impl<typename type_list_split<List, N>::first_list,
typename type_list_split<List, N>::second_list> {};

// merge_sorted
template<typename SortedList1, typename SortedList2, template<typename, typename> typename Pred>
struct type_list_merge_sorted_impl;
Expand Down
27 changes: 9 additions & 18 deletions src/core/include/mp-units/bits/unit_magnitude.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,35 +76,26 @@ struct power_v {
static constexpr ratio exponent{Num, Den...};
};

template<typename Element>
[[nodiscard]] consteval auto get_base(Element element)
template<typename T>
[[nodiscard]] consteval auto get_base(T element)
{
if constexpr (is_specialization_of_v<Element, power_v>)
return Element::base;
if constexpr (is_specialization_of_v<T, power_v>)
return T::base;
else
return element;
}

template<typename Element>
[[nodiscard]] consteval auto get_base_value(Element element)
template<typename T>
[[nodiscard]] consteval auto get_base_value(T element)
{
if constexpr (is_specialization_of_v<Element, power_v>)
return get_base_value(Element::base);
else if constexpr (MagConstant<Element>)
if constexpr (is_specialization_of_v<T, power_v>)
return get_base_value(T::base);
else if constexpr (MagConstant<T>)
return element._value_;
else
return element;
}

template<typename Element>
[[nodiscard]] MP_UNITS_CONSTEVAL ratio get_exponent(Element)
{
if constexpr (is_specialization_of_v<Element, power_v>)
return Element::exponent;
else
return ratio{1};
}

template<auto V, ratio R>
[[nodiscard]] consteval auto power_v_or_T()
{
Expand Down
4 changes: 2 additions & 2 deletions src/core/include/mp-units/framework/quantity.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ concept ValuePreservingTo = Representation<std::remove_cvref_t<FromRep>> && Repr

template<typename QFrom, typename QTo>
concept QuantityConvertibleTo =
Quantity<QFrom> && Quantity<QTo> && QuantitySpecConvertibleTo<QFrom::quantity_spec, QTo::quantity_spec> &&
UnitConvertibleTo<QFrom::unit, QTo::unit> &&
Quantity<QFrom> && Quantity<QTo> && implicitly_convertible(QFrom::quantity_spec, QTo::quantity_spec) &&
(interconvertible(QFrom::unit, QTo::unit)) &&
ValuePreservingTo<typename QFrom::rep, typename QTo::rep, QFrom::unit, QTo::unit> &&
// TODO consider providing constraints of sudo_cast here rather than testing if it can be called (its return type is
// deduced thus the function is evaluated here and may emit truncating conversion or other warnings)
Expand Down
4 changes: 2 additions & 2 deletions src/core/include/mp-units/framework/quantity_cast.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ namespace mp_units {
* @tparam ToQS a quantity specification to use for a target quantity
*/
template<QuantitySpec auto ToQS, typename FwdQ, Quantity Q = std::remove_cvref_t<FwdQ>>
requires detail::QuantitySpecCastableTo<Q::quantity_spec, ToQS>
requires(castable(Q::quantity_spec, ToQS)) && detail::WeakUnitOf<MP_UNITS_NONCONST_TYPE(Q::unit), ToQS>
[[nodiscard]] constexpr Quantity auto quantity_cast(FwdQ&& q)
{
return quantity{std::forward<FwdQ>(q).numerical_value_is_an_implementation_detail_, make_reference(ToQS, Q::unit)};
Expand All @@ -81,7 +81,7 @@ template<QuantitySpec auto ToQS, typename FwdQ, Quantity Q = std::remove_cvref_t
* @tparam ToQS a quantity specification to use for a target quantity point
*/
template<QuantitySpec auto ToQS, typename FwdQP, QuantityPoint QP = std::remove_cvref_t<FwdQP>>
requires detail::QuantitySpecCastableTo<QP::quantity_spec, ToQS>
requires(castable(QP::quantity_spec, ToQS)) && detail::WeakUnitOf<MP_UNITS_NONCONST_TYPE(QP::unit), ToQS>
[[nodiscard]] constexpr QuantityPoint auto quantity_cast(FwdQP&& qp)
{
return QP{quantity_cast<ToQS>(std::forward<FwdQP>(qp).quantity_from_origin_is_an_implementation_detail_),
Expand Down
8 changes: 4 additions & 4 deletions src/core/include/mp-units/framework/quantity_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ concept QuantityLikeImpl = requires(const T& qty, const Traits<T>::rep& num) {
} // namespace detail

/**
* @brief A concept matching all quantities with provided quantity spec
* @brief A concept matching all quantities of the provided quantity spec
*
* Satisfied by all quantities with a quantity_spec being the instantiation derived from
* the provided quantity_spec type.
* Satisfied by all quantities with the reference satisfying @c ReferenceOf<QS>.
*/
MP_UNITS_EXPORT template<typename Q, auto QS>
concept QuantityOf = Quantity<Q> && QuantitySpecOf<MP_UNITS_NONCONST_TYPE(Q::quantity_spec), QS>;
concept QuantityOf = Quantity<Q> && QuantitySpec<MP_UNITS_REMOVE_CONST(decltype(QS))> &&
ReferenceOf<MP_UNITS_NONCONST_TYPE(Q::reference), QS>;

/**
* @brief A concept matching all external quantities like types
Expand Down
4 changes: 2 additions & 2 deletions src/core/include/mp-units/framework/quantity_point_concepts.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ concept SameAbsolutePointOriginAs =


/**
* @brief A concept matching all quantity points with provided quantity spec
* @brief A concept matching all quantity points of the provided property
*
* Satisfied by all quantity points with a quantity_spec being the instantiation derived from
* the provided quantity_spec type, or quantity points having the origin with the same
* `absolute_point_origin`.
*/
MP_UNITS_EXPORT template<typename QP, auto V>
concept QuantityPointOf =
QuantityPoint<QP> && (QuantitySpecOf<MP_UNITS_NONCONST_TYPE(QP::quantity_spec), V> ||
QuantityPoint<QP> && (ReferenceOf<MP_UNITS_NONCONST_TYPE(QP::reference), V> ||
detail::SameAbsolutePointOriginAs<MP_UNITS_NONCONST_TYPE(QP::absolute_point_origin), V>);

/**
Expand Down
Loading

0 comments on commit 74988f6

Please sign in to comment.