diff --git a/au/quantity_point.hh b/au/quantity_point.hh index db4698f4..a8de99fa 100644 --- a/au/quantity_point.hh +++ b/au/quantity_point.hh @@ -121,55 +121,41 @@ class QuantityPoint { template ::value>> + typename = std::enable_if_t>::value>> constexpr auto as(NewUnit u) const { - return make_quantity_point(this->template in(u)); + return make_quantity_point>(this->template in(u)); } - template ::value>> + template >::value>> constexpr auto as(NewUnit u) const { - return make_quantity_point(in(u)); + return make_quantity_point>(in(u)); } template ::value>> + typename = std::enable_if_t>::value>> constexpr NewRep in(NewUnit u) const { using CalcRep = typename detail::IntermediateRep::type; return (rep_cast(x_) - - rep_cast(OriginDisplacement::value())) - .template in(u); + rep_cast( + OriginDisplacement>::value())) + .template in(associated_unit_for_points(u)); } - template ::value>> + template >::value>> constexpr Rep in(NewUnit u) const { - static_assert(detail::OriginDisplacementFitsIn::value, - "Cannot represent origin displacement in desired Rep"); + static_assert( + detail::OriginDisplacementFitsIn, Unit>::value, + "Cannot represent origin displacement in desired Rep"); // `rep_cast` is needed because if these are integral types, their difference might become a // different type due to integer promotion. - return rep_cast(x_ + rep_cast(OriginDisplacement::value())).in(u); - } - - // Overloads for passing a QuantityPointMaker. - // - // This is the "magic" that lets us write things like `position.in(meters_pt)`, instead of just - // `position.in(Meters{})`. - template - constexpr auto as(QuantityPointMaker) const { - return as(NewUnit{}); - } - template - constexpr auto as(QuantityPointMaker) const { - return as(NewUnit{}); - } - template - constexpr NewRep in(QuantityPointMaker) const { - return in(NewUnit{}); - } - template - constexpr Rep in(QuantityPointMaker) const { - return in(NewUnit{}); + return rep_cast( + x_ + rep_cast( + OriginDisplacement, Unit>::value())) + .in(associated_unit_for_points(u)); } // "Old-style" overloads with template parameters, and no function parameters. @@ -312,6 +298,9 @@ struct QuantityPointMaker { } }; +template +struct AssociatedUnitForPoints> : stdx::type_identity {}; + // Type trait to detect whether two QuantityPoint types are equivalent. // // In this library, QuantityPoint types are "equivalent" exactly when they use the same Rep, and are diff --git a/au/unit_of_measure.hh b/au/unit_of_measure.hh index 62110c2b..20b2c8de 100644 --- a/au/unit_of_measure.hh +++ b/au/unit_of_measure.hh @@ -145,6 +145,11 @@ struct AssociatedUnit : stdx::type_identity {}; template using AssociatedUnitT = typename AssociatedUnit::type; +template +struct AssociatedUnitForPoints : stdx::type_identity {}; +template +using AssociatedUnitForPointsT = typename AssociatedUnitForPoints::type; + // `CommonUnitT`: the largest unit that evenly divides all input units. // // A specialization will only exist if all input types are units. @@ -249,6 +254,11 @@ constexpr auto associated_unit(U) { return AssociatedUnitT{}; } +template +constexpr auto associated_unit_for_points(U) { + return AssociatedUnitForPointsT{}; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Unit arithmetic traits: products, powers, and derived operations. diff --git a/docs/discussion/idioms/unit-slots.md b/docs/discussion/idioms/unit-slots.md index f2c1446a..56d406b8 100644 --- a/docs/discussion/idioms/unit-slots.md +++ b/docs/discussion/idioms/unit-slots.md @@ -57,6 +57,44 @@ they have two advantages that make them easier to read: 2. You can use grammatically correct names, such as `meters / squared(second)` (note: `second` is singular), rather than `Meters{} / squared(Seconds{})`. +### Other expressions + +There are other monovalue types that would feel right at home in a unit slot. We typically support +those too! Key examples include [unit symbols](../../reference/unit.md#unit-symbols) and +[constants](../../reference/constant.md). Expand the block below to see a worked example. + +??? example "Example: using unit symbols and constants in unit slots" + Suppose we have the following preamble, simply to set everything up. + + ```cpp + struct SpeedOfLight : decltype(Meters{} / Seconds{} * mag<299'792'458>()) { + static constexpr const char label[] = "c"; + }; + constexpr const char SpeedOfLight::label[]; + constexpr auto c = make_constant(SpeedOfLight{}); + + // These using declarations should be in a `.cc` file, not `.hh`, + // to avoid namespace pollution! + using symbols::m; + using symbols::s; + ``` + + Then we can pass either the unit symbols, or the constants, to our unit slot APIs: + + ```cpp + constexpr auto v = (miles / hour)(65.0); + + std::cout << v.as(m / s) << std::endl; + // ^^^^^ + // Passing a unit symbol to the unit slot. Output: + // "29.0576 m / s" + + std::cout << v.as(c) << std::endl; + // ^ + // Passing a constant to the unit slot. Output: + // "9.69257e-08 c" + ``` + #### Notes for `QuantityPoint` `QuantityPoint` doesn't use quantity makers: it uses quantity _point_ makers. For example, instead @@ -67,6 +105,12 @@ use the quantity _point_ maker instead of the _quantity_ maker. The library wil automatically: for example, you can't pass `meters` to a `QuantityPoint`'s unit slot, and you can't pass `meters_pt` to a `Quantity`'s unit slot. +To get the associated unit for a type, use the +[`AssociatedUnitT`](../../reference/unit.md#associated-unit) trait when you're dealing with +`Quantity`, and use the +[`AssociatedUnitForPointsT`](../../reference/unit.md#associated-unit-for-points) trait when dealing +with `QuantityPoint`. + ## Examples: rounding to RPM Let's look at some examples, using this quantity variable: diff --git a/docs/reference/unit.md b/docs/reference/unit.md index 7b5a1923..cdc949b0 100644 --- a/docs/reference/unit.md +++ b/docs/reference/unit.md @@ -495,12 +495,10 @@ $273.15 \,\text{K}$. - For _instances_ `u1` and `u2`: - `origin_displacement(u1, u2)` -### Associated unit +### Associated unit {#associated-unit} -**Result:** The actual unit associated with a "unit-alike". - -What's a "unit-alike"? It's something that can be passed to an API expecting the name of a unit. -Here are a few examples. +**Result:** The actual unit associated with a [unit slot](../discussion/idioms/unit-slots.md) that +is associated with a `Quantity` type. Here are a few examples. ```cpp round_in(meters, feet(20)); @@ -508,16 +506,21 @@ round_in(meters, feet(20)); round_in(Meters{}, feet(20)); // ^^^^^^^^ +using symbols::m; +round_in(m, feet(20)); +// ^ + feet(6).in(inches); // ^^^^^^ feet(6).in(Inches{}); // ^^^^^^^^ ``` -The underlined arguments are all unit-alikes. In practice, a unit-alike either a `QuantityMaker` -for some unit, or a unit itself. +The underlined arguments are all unit slots. The kinds of things that can be passed here include +a `QuantityMaker` for a unit, a [constant](./constant.md), a [unit symbol](#unit-symbols), or simply +a unit type itself. -The use case for this trait is to _implement_ a function that takes a unit-alike. +The use case for this trait is to _implement_ the unit slot argument for a function. **Syntax:** @@ -526,6 +529,36 @@ The use case for this trait is to _implement_ a function that takes a unit-alike - For an _instance_ `u`: - `associated_unit(u)` +### Associated unit (for points) {#associated-unit-for-points} + +**Result:** The actual unit associated with a [unit slot](../discussion/idioms/unit-slots.md) that +is associated with a quantity point type. Here are a few examples. + +```cpp +round_in(meters_pt, milli(meters_pt)(1200)); +// ^^^^^^^^^ +round_in(Meters{}, milli(meters_pt)(1200)); +// ^^^^^^^^ + +meters_pt(6).in(centi(meters_pt)); +// ^^^^^^^^^^^^^^^^ +meters_pt(6).in(Centi{}); +// ^^^^^^^^^^^^^^^ +``` + +The underlined arguments are unit slots for quantity points. In practice, this will be either +a `QuantityPointMaker` for some unit, or a unit itself. + +The use case for this trait is to _implement_ a function or API that takes a unit slot, and is +associated with quantity points. + +**Syntax:** + +- For a _type_ `U`: + - `AssociatedUnitForPointsT` +- For an _instance_ `u`: + - `associated_unit_for_points(u)` + ### Common unit **Result:** The largest unit that evenly divides its input units. (Read more about the concept of