diff --git a/au/BUILD.bazel b/au/BUILD.bazel index bb05771c..8d34a91d 100644 --- a/au/BUILD.bazel +++ b/au/BUILD.bazel @@ -150,17 +150,23 @@ cc_test( ) cc_library( - name = "operators", - hdrs = ["operators.hh"], + name = "constant", + hdrs = ["constant.hh"], + deps = [ + ":quantity", + ":unit_of_measure", + ":wrapper_operations", + ], ) cc_test( - name = "operators_test", + name = "constant_test", size = "small", - srcs = ["operators_test.cc"], + srcs = ["constant_test.cc"], deps = [ - ":operators", + ":constant", ":testing", + ":units", "@com_google_googletest//:gtest_main", ], ) @@ -263,6 +269,22 @@ cc_test( ], ) +cc_library( + name = "operators", + hdrs = ["operators.hh"], +) + +cc_test( + name = "operators_test", + size = "small", + srcs = ["operators_test.cc"], + deps = [ + ":operators", + ":testing", + "@com_google_googletest//:gtest_main", + ], +) + cc_library( name = "packs", hdrs = ["packs.hh"], diff --git a/au/constant.hh b/au/constant.hh new file mode 100644 index 00000000..84fc788b --- /dev/null +++ b/au/constant.hh @@ -0,0 +1,105 @@ +// Copyright 2023 Aurora Operations, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "au/quantity.hh" +#include "au/quantity_point.hh" +#include "au/stdx/type_traits.hh" +#include "au/unit_of_measure.hh" +#include "au/wrapper_operations.hh" + +namespace au { + +// +// A monovalue type to represent a constant value, including its units, if any. +// +// Users can multiply or divide `Constant` instances by raw numbers or `Quantity` instances, and it +// will perform symbolic arithmetic at compile time without affecting the stored numeric value. +// `Constant` also composes with other constants, and with `QuantityMaker` and other related types. +// +// Although `Constant` does not have any specific numeric type associated with it (as opposed to +// `Quantity`), it can easily convert to any appropriate `Quantity` type, with any rep. Unlike +// `Quantity`, these conversions support _exact_ safety checks, so that every conversion producing a +// correctly representable value will succeed, and every unrepresentable conversion will fail. +// +template +struct Constant : detail::MakesQuantityFromNumber, + detail::ScalesQuantity, + detail::ComposesWith, + detail::ComposesWith, + detail::ComposesWith, + detail::ForbidsComposingWith, + detail::ForbidsComposingWith, + detail::CanScaleByMagnitude { + // Convert this constant to a Quantity of the given rep. + template + constexpr auto as() const { + return make_quantity(static_cast(1)); + } + + // Convert this constant to a Quantity of the given unit and rep, ignoring safety checks. + template + constexpr auto coerce_as(OtherUnit u) const { + return as().coerce_as(u); + } + + // Convert this constant to a Quantity of the given unit and rep. + template + constexpr auto as(OtherUnit u) const { + static_assert(can_store_value_in(u), "Cannot represent constant in this unit/rep"); + return coerce_as(u); + } + + // Get the value of this constant in the given unit and rep, ignoring safety checks. + template + constexpr auto coerce_in(OtherUnit u) const { + return as().coerce_in(u); + } + + // Get the value of this constant in the given unit and rep. + template + constexpr auto in(OtherUnit u) const { + static_assert(can_store_value_in(u), "Cannot represent constant in this unit/rep"); + return coerce_in(u); + } + + // Implicitly convert to any quantity type which passes safety checks. + template + constexpr operator Quantity() const { + return as(U{}); + } + + // Static function to check whether this constant can be exactly-represented in the given rep + // `T` and unit `OtherUnit`. + template + static constexpr bool can_store_value_in(OtherUnit other) { + return representable_in(unit_ratio(Unit{}, other)); + } +}; + +// Make a constant from the given unit. +// +// Note that the argument is a _unit slot_, and thus can also accept things like `QuantityMaker` and +// `SymbolFor` in addition to regular units. +template +constexpr Constant> make_constant(UnitSlot) { + return {}; +} + +// Support using `Constant` in a unit slot. +template +struct AssociatedUnit> : stdx::type_identity {}; + +} // namespace au diff --git a/au/constant_test.cc b/au/constant_test.cc new file mode 100644 index 00000000..12fd2b00 --- /dev/null +++ b/au/constant_test.cc @@ -0,0 +1,266 @@ +// Copyright 2023 Aurora Operations, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "au/constant.hh" + +#include + +#include "au/testing.hh" +#include "au/units/joules.hh" +#include "au/units/meters.hh" +#include "au/units/newtons.hh" +#include "au/units/radians.hh" +#include "au/units/seconds.hh" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +using ::testing::AnyOf; +using ::testing::StaticAssertTypeEq; +using ::testing::StrEq; + +namespace au { +namespace { + +template +std::string stream_to_string(Quantity q) { + std::ostringstream oss; + + // Set the precision to the full precision of R. + oss.precision(std::numeric_limits::digits10 + 1); + + oss << q; + return oss.str(); +} + +// Ad hoc Constant for the speed of light, along with associated variables. +constexpr auto C_MPS = mag<299'792'458>(); +struct SpeedOfLight : decltype(Meters{} / Seconds{} * C_MPS) { + static constexpr const char label[] = "c"; +}; +constexpr const char SpeedOfLight::label[]; +constexpr auto speed_of_light = QuantityMaker{}; +constexpr auto c = make_constant(speed_of_light); + +// Ad hoc Constant for Planck's constant. +constexpr auto H_JS = mag<6'626'070'15>() / mag<1'000'000'00>() * pow<-34>(mag<10>()); +struct PlancksConstant : decltype(Joules{} * Seconds{} * H_JS) { + static constexpr const char label[] = "h"; +}; +constexpr auto plancks_constant = QuantityMaker{}; +constexpr auto h = make_constant(plancks_constant); +} // namespace + +TEST(MakeConstant, MakesConstantFromUnit) { + StaticAssertTypeEq>(); +} + +TEST(MakeConstant, MakesConstantFromQuantityMaker) { + StaticAssertTypeEq>(); +} + +TEST(MakeConstant, MakesAdHocConstantFromQuantityMaker) { + constexpr auto ad_hoc_c = make_constant(meters / second * mag<299'792'458>()); + EXPECT_THAT((1.0 * ad_hoc_c).in(meters / second), SameTypeAndValue(299'792'458.0)); + + auto foo = [](Quantity, int> q) { std::cout << q << std::endl; }; + foo(c); +} + +TEST(MakeConstant, MakesConstantFromSymbol) { + constexpr auto m = symbol_for(meters); + constexpr auto s = symbol_for(seconds); + constexpr auto ad_hoc_c = mag<299'792'458>() * make_constant(m / s); + EXPECT_THAT(123 * ad_hoc_c, QuantityEquivalent(123 * c)); +} + +TEST(Constant, CanGetQuantityBySpecifyingRep) { + EXPECT_THAT(c.as(), SameTypeAndValue(c * 1.0f)); + EXPECT_THAT(c.as(), SameTypeAndValue(c * 1)); +} + +TEST(Constant, CanGetQuantityInSpecifiedUnitAndRep) { + EXPECT_THAT(c.in(meters / second), SameTypeAndValue(get_value(C_MPS))); + EXPECT_THAT(c.as(meters / second), + SameTypeAndValue((meters / second)(get_value(C_MPS)))); +} + +TEST(Constant, UsesExactSafetyChecksInsteadOfHeuristics) { + EXPECT_THAT(c.in(meters / second), SameTypeAndValue(get_value(C_MPS))); + EXPECT_THAT(c.as(meters / second), + SameTypeAndValue((meters / second)(get_value(C_MPS)))); + + // The following code must not compile, since the speed of light in m/s can't fit in `int16_t`. + // + // Uncomment the following to test: + // c.as(meters / second); +} + +TEST(Constant, CanCoerce) { + EXPECT_THAT(c.coerce_in(kilo(meters) / second), SameTypeAndValue(299'792)); + EXPECT_THAT(c.coerce_as(kilo(meters) / second), + SameTypeAndValue((kilo(meters) / second)(299'792))); +} + +TEST(Constant, MakesQuantityWhenPostMultiplyingNumericValue) { + EXPECT_THAT(3.f * c, SameTypeAndValue(speed_of_light(3.f))); +} + +TEST(Constant, MakesQuantityWhenPreMultiplyingNumericValue) { + EXPECT_THAT((c * 2).coerce_as(meters / second), + SameTypeAndValue((meters / second)(get_value(mag<2>() * C_MPS)))); +} + +TEST(Constant, MakesQuantityWhenDividingIntoNumericValue) { + EXPECT_THAT(20u / c, SameTypeAndValue(inverse(speed_of_light)(20u))); +} + +TEST(Constant, MakesQuantityWhenDividedByNumericValue) { + EXPECT_THAT((c / 2.0).as(meters / second), + SameTypeAndValue((meters / second)(get_value(C_MPS / mag<2>())))); + + // The following must not compile, because it would use integer division with an implicit + // numerator of `1`, and would therefore always be zero. + // + // Uncomment to make sure the compilation fails: + // c / 2; +} + +TEST(Constant, AppliesConstantSymbolToUnitLabel) { + constexpr auto lambda = nano(meters)(512.0); + EXPECT_THAT(stream_to_string(h * c / lambda), + AnyOf(StrEq("0.001953125 (h * c) / nm"), StrEq("0.001953125 (c * h) / nm"))); +} + +TEST(Constant, MakesScaledConstantWhenPostMultipliedByMagnitude) { + StaticAssertTypeEq()), Constant())>>(); +} + +TEST(Constant, MakesScaledConstantWhenDividedByMagnitude) { + StaticAssertTypeEq()), Constant())>>(); +} + +TEST(Constant, MakesScaledConstantWhenPreMultipliedByMagnitude) { + StaticAssertTypeEq>(); +} + +TEST(Constant, MakesScaledInverseConstantWhenDividedIntoMagnitude) { + StaticAssertTypeEq{} * PI)>>(); +} + +TEST(Constant, ChangesUnitsForQuantityWhenPostMultiplying) { + EXPECT_THAT(newtons(5.f) * c, SameTypeAndValue((newton * speed_of_light)(5.f))); +} + +TEST(Constant, ChangesUnitsForQuantityWhenDividingIt) { + EXPECT_THAT(joules(8) / c, SameTypeAndValue((joules / speed_of_light)(8))); +} + +TEST(Constant, ChangesUnitsForQuantityWhenPreMultiplying) { + EXPECT_THAT(c * seconds(5.f), SameTypeAndValue((speed_of_light * seconds)(5.f))); +} + +TEST(Constant, ChangesUnitsForQuantityWhenDividedIntoIt) { + EXPECT_THAT(c / meters(4.0), SameTypeAndValue((speed_of_light / meter)(0.25))); + + // The following must not compile, because it would use integer division with an implicit + // numerator of `1`, and would therefore always be zero. + // + // Uncomment to make sure the compilation fails: + // EXPECT_THAT(c / meters(4), SameTypeAndValue((speed_of_light / meter)(0.25))); +} + +TEST(Constant, ChangesUnitsForQuantityMakerWhenPostMultiplying) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForQuantityMakerWhenDividingIt) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForQuantityMakerWhenPreMultiplying) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForQuantityMakerWhenDividedIntoIt) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForSingularNameWhenPostMultiplying) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForSingularNameWhenDividingIt) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForSingularNameWhenPreMultiplying) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ChangesUnitsForSingularNameWhenDividedIntoIt) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ComposesViaMultiplication) { + StaticAssertTypeEq>(); +} + +TEST(Constant, SupportsMultiplyingConstantByItself) { + StaticAssertTypeEq>(); +} + +TEST(Constant, ComposesViaDivision) { + StaticAssertTypeEq>(); +} + +TEST(Constant, FailsToCompileWhenMultiplyingOrDividingWithQuantityPoint) { + // Uncomment each line below individually to verify. + + // make_constant(meters) * meters_pt(1); + // make_constant(meters) / meters_pt(1); + // meters_pt(1) * make_constant(meters); + // meters_pt(1) / make_constant(meters); + + // make_constant(meters) * meters_pt; + // make_constant(meters) / meters_pt; + // meters_pt * make_constant(meters); + // meters_pt / make_constant(meters); +} + +TEST(Constant, ImplicitlyConvertsToAppropriateQuantityTypes) { + constexpr QuantityI32 v = c; + EXPECT_THAT(v, SameTypeAndValue((meters / second)(get_value(C_MPS)))); + + // The following must not compile. Uncomment inside the scope to check. + { + // constexpr Quantity v = c; + // (void)c; + } +} + +TEST(Constant, SupportsUnitSlotAPIs) { + constexpr auto three_c_mps = (3.f * c).as(meters / second); + EXPECT_THAT(three_c_mps.in(c), SameTypeAndValue(3.f)); +} + +TEST(CanStoreValueIn, ChecksRangeOfTypeForIntegers) { + EXPECT_TRUE(decltype(c)::can_store_value_in(meters / second)); + EXPECT_FALSE(decltype(c)::can_store_value_in(meters / second)); +} + +} // namespace au diff --git a/docs/alternatives/index.md b/docs/alternatives/index.md index 9f5e12b3..584dbf10 100644 --- a/docs/alternatives/index.md +++ b/docs/alternatives/index.md @@ -888,17 +888,24 @@ features. - Includes built-in constants as quantities - Includes built-in constants as quantities + Includes built-in constants as quantities + Includes built-in constants as quantities - + "Faster than lightspeed" constants - - Plan to support someday; see - #90. + +
    +
  • Constants as types
  • +
  • Perfect conversion policy
  • +
  • Implicit Quantity conversion
  • +
  • + No built-in values yet (see #90) +
  • +
diff --git a/docs/reference/constant.md b/docs/reference/constant.md new file mode 100644 index 00000000..aaf3ba8e --- /dev/null +++ b/docs/reference/constant.md @@ -0,0 +1,278 @@ +# Constant + +`Constant` is a family of template types, each of which represents a single specific quantity value. + +Recall that the usual way to represent quantity values is with a [`Quantity`](./quantity.md) +type. This holds a numeric variable of type `Rep` inside, letting it represent +many possible quantity values. By contrast, `Constant` is an empty class and has no "rep": the +single value it can represent is fully encoded in its type. This makes it an example of +a [monovalue type](./detail/monovalue_types.md). + +Because the value is always fully known at compile time, we do not need to use a heuristic like the +overflow safety surface to determine which conversions are allowed. Instead, we can achieve +a perfect conversion policy: we allow converting to any `Quantity` that can represent the value +exactly, and disallow all other conversions. + +The main use of `Constant` is to multiply and divide raw numbers or `Quantity` values. When we do +this, the constant is applied _symbolically_, and affects the _units_ of the resulting quantity. +For example, multiplying a duration in seconds by a constant representing the speed of light +produces a _length_, measured in units of _light-seconds_. Notably, _the underlying stored numeric +value does not change_: whether a duration of `5` seconds, or a length of `5` light-seconds, we +still store `5` under the hood. + +This approach means that if subsequent operations cancel out the constant, this cancellation is both +_exact_ and has _zero runtime cost_. + +## Constructing `Constant` + +`Constant` encodes all information about the value in its type. Moreover, it has only a single +template parameter, which is a [unit](./unit.md). Therefore, the first step is to encode your +quantity as a unit --- that is, to define the unit "U" such that your quantity has a value of +"1 U". + +To do this, follow [the usual instructions for creating new units](../howto/new-units.md). Note +that you can use a much simpler definition that omits most of the optional features. The only +important ones are those labeled `[1]` (the strong type definition) and `[2]` (the unit label). + +Having defined your unit, you can pass an instance to the `make_constant` function. If the unit you +defined above is called `YourUnits`, and the constant is called `YOUR_CONSTANT`, then the constant +definition will look like this: + +```cpp +constexpr auto YOUR_CONSTANT = make_constant(YourUnits{}); +``` + +Finally, note that the argument to `make_constant()` is a [unit +slot](../discussion/idioms/unit-slots.md), so you can pass "unit-like" alternatives such as +`QuantityMaker` or `SymbolFor` instances as well. + +??? example "Full worked example: speed of light" + Let's look at an example of defining a constant for the speed of light. Both the name of the + instance and the label will be `c`. + + === "C++14" + + ```cpp + // In .hh file: + struct SpeedOfLight : decltype(Meters{} / Seconds{} * mag<299'792'458>()) { + static constexpr const char label[] = "c"; + }; + + constexpr auto c = make_constant(SpeedOfLight{}); + ``` + + ```cpp + // In .cc file: + constexpr const char SpeedOfLight::label[]; + ``` + + === "C++17 or later" + + ```cpp + // In .hh file: + struct SpeedOfLight : decltype(Meters{} / Seconds{} * mag<299'792'458>()) { + static constexpr inline const char label[] = "c"; + }; + + constexpr auto c = make_constant(SpeedOfLight{}); + ``` + +### Ad hoc constants + +You can obtain many of the benefits of `Constant` even if you don't formally define a new unit. +Because `make_constant` has a unit slot API, you can pass an ad hoc expression to it. For example: + +```cpp +constexpr auto c = make_constant(meters / second * mag<299'792'458>()); +``` + +The main advantage of doing this is its conciseness: the constant definition is a single, readable +line. The built constant also has all of the multiplication and division operators types that +`Constant` supports, as well as its perfect conversion policy to any `Quantity` type. + +The only disadvantage is the missing label, which will make printed quantities hard to understand +because the constant will be represented as `[UNLABELED_UNIT]` in the compound label. + +If the constant is used in multiple translation units, or if it leads to values that are printed +out, we believe this disadvantage outweighs the benefits, and we recommend a full definition with +a new unit. Otherwise, the ad hoc constant approach may be called for. + +## `Constant` and unit slots + +`Constant` can be passed to any API that takes a [unit slot](../discussion/idioms/unit-slots.md). + +## Converting to `Quantity` + +`Constant` can be converted to any `Quantity` type of the same dimension. + +By default, this conversion policy is _perfect_. This means that it permits converting to any +`Quantity` that can represent the value exactly, and disallows all other conversions. Users can +also override this policy by choosing the "coerce" variant of any API (say, using `.coerce_as()` +instead of `.as()`). + +Finally, it's important to appreciate that `Constant` has no rep, no underlying numeric type. +Therefore, every `Quantity` conversion API requires an explicit template parameter to specify the +desired rep. + +### `.as()` + +This function expresses the constant as a `Quantity` in "units of this constant". Therefore, the +underlying stored value will be `T{1}`, and the rep will be `T`. + +### `.as(unit)` {#as-T-unit} + +This function expresses the constant as a `Quantity` in the requested unit, using a rep of `T`. It +has a perfect conversion policy, which means that it compiles if and only if the constant's value in +the requested unit can be exactly represented in the type `T`. + +The argument `unit` is a [unit slot](../discussion/idioms/unit-slots.md) API, so it accepts a unit +instance, quantity maker instance, or any other instance compatible with a unit slot. + +### `.coerce_as(unit)` + +This function expresses the constant as a `Quantity` in the requested unit, using a rep of `T`. It +is similar to [`.as(unit)`](#as-T-unit), except that it will ignore the safety checks that +prevent truncation and overflow. + +!!! warning + Because `.as(unit)` has a perfect conversion policy, we know that this function either + produces the exact same result (in which case you could simply _call_ `.as(unit)`), _or_ it + produces a result which is **guaranteed to be lossy**. Therefore, be very judicious in using + this function. + +### `.in(unit)` {#in-T-unit} + +This function produces a raw numeric value, of type `T`, holding the value of the constant in the +requested unit. It has a perfect conversion policy, which means that it compiles if and only if the +constant's value in the requested unit can be exactly represented in the type `T`. + +The argument `unit` is a [unit slot](../discussion/idioms/unit-slots.md) API, so it accepts a unit +instance, quantity maker instance, or any other instance compatible with a unit slot. + +### `.coerce_in(unit)` + +This function produces a raw numeric value, of type `T`, holding the value of the constant in the +requested unit. It is similar to [`.in(unit)`](#in-T-unit), except that it will ignore the +safety checks that prevent truncation and overflow. + +!!! warning + Because `.in(unit)` has a perfect conversion policy, we know that this function either + produces the exact same result (in which case you could simply _call_ `.in(unit)`), _or_ it + produces a result which is **guaranteed to be lossy**. Therefore, be very judicious in using + this function. + +### Implicit `Quantity` conversion + +`Constant` will implicitly convert to any `Quantity` type which passes the safety checks on +truncation and overflow. Essentially: any time [`.as(unit)`](#as-T-unit) produces a result, that +same result can be obtained via implicit conversion. + +This provides great flexibility and confidence in passing `Constant` values to APIs that take +`Quantity`. + +!!! note + The fact that `Constant` has a perfect conversion policy means that we can use it with APIs + where the corresponding `Quantity` would not work, because `Quantity` is forced to use the + overflow safety surface, which is a more conservative heuristic. + + For example, suppose you have an API accepting `Quantity, int>`, + and a constant `c` representing the speed of light. + + You will be able to pass `c` to this API, because the constant-to-quantity conversion operation + knows the exact value at compile time, and can verify that it fits in an `int`. + + By contrast, you would not be able to pass `c.as()` (which is a `Quantity`). Even though + it would work for _this specific value_ (which is `1`), this quantity-to-quantity conversion is + too dangerous for `int` in general. + +## Operations + +Each operation with a `Constant` consists in multiplying or dividing with some other family of +types. + +### Raw numeric type `T` + +Multiplying or dividing `Constant` with a raw numeric type `T` produces a `Quantity` whose rep +is `T`, and whose unit is derived from `Unit`. + +In the following table, we will use `x` to represent the value that was stored in the input of type +`T`. + +| Operation | Resulting Type | Underlying Value | Notes | +| --------- | -------------- | ---------------- | ----- | +| `Constant * T` | `Quantity` | `x` | | +| `Constant / T` | `Quantity` | `T{1} / x` | Disallowed for integral `T` | +| `T * Constant` | `Quantity` | `x` | | +| `T / Constant` | `Quantity, T>` | `x` | | + +### `Quantity` + +Multiplying or dividing `Constant` with a `Quantity` produces a `Quantity` whose rep is +`R`, and whose unit is derived from `Unit` and `U`. + +In the following table, we will use `x` to represent the underlying value in the input quantity --- +that is, if the input quantity was `q`, then `x` is `q.in(U{})`. + +| Operation | Resulting Type | Underlying Value | Notes | +| --------- | -------------- | ---------------- | ----- | +| `Constant * Quantity` | `Quantity, R>` | `x` | | +| `Constant / Quantity` | `Quantity, R>` | `R{1} / x` | Disallowed for integral `R` | +| `Quantity * Constant` | `Quantity, R>` | `x` | | +| `Quantity / Constant` | `Quantity, R>` | `x` | | + +### `Constant` + +Constants compose: the product or quotient of two `Constant` instances is a new `Constant` instance. + +| Operation | Resulting Type | +| --------- | -------------- | +| `Constant * Constant` | `Constant>` | +| `Constant / Constant` | `Constant>` | + +### `QuantityMaker` + +Multiplying or dividing `Constant` with a `QuantityMaker` produces a new `QuantityMaker` +whose unit is derived from `Unit` and `U`. + +| Operation | Resulting Type | +| --------- | -------------- | +| `Constant * QuantityMaker` | `QuantityMaker>` | +| `Constant / QuantityMaker` | `QuantityMaker>` | +| `QuantityMaker * Constant` | `QuantityMaker>` | +| `QuantityMaker / Constant` | `QuantityMaker>` | + +### `SingularNameFor` + +Multiplying or dividing `Constant` with a `SingularNameFor` produces a new +`SingularNameFor` whose unit is derived from `Unit` and `U`. + +| Operation | Resulting Type | +| --------- | -------------- | +| `Constant * SingularNameFor` | `SingularNameFor>` | +| `Constant / SingularNameFor` | `SingularNameFor>` | +| `SingularNameFor * Constant` | `SingularNameFor>` | +| `SingularNameFor / Constant` | `SingularNameFor>` | + +### `Magnitude` + +Multiplying or dividing `Constant` with a `Magnitude` produces a new `Constant` which is +scaled by that magnitude. + +In the following table, let `m` be an instance of `Magnitude`. + +| Operation | Resulting Type | +| --------- | -------------- | +| `Constant * Magnitude` | `Constant` | +| `Constant / Magnitude` | `Constant` | +| `Magnitude * Constant` | `Constant` | +| `Magnitude / Constant` | `Constant{} * m)>` | + +### `QuantityPointMaker` (deleted) + +Multiplying or dividing `Constant` with a `QuantityPointMaker` is explicitly deleted, +because quantity points do not support multiplication. + +### `QuantityPoint` (deleted) + +Multiplying or dividing `Constant` with a `QuantityPoint` is explicitly deleted, +because quantity points do not support multiplication. diff --git a/docs/reference/index.md b/docs/reference/index.md index 137f155e..c6b655a3 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -16,6 +16,10 @@ Here's a guide to the main reference pages. a _displacement_). Practically speaking, this is **essential for dealing with temperatures**, and useful for a couple other dimensions such as pressures and distances. +- **[`Constant`](./constant.md).** A constant quantity which is known at compile time, and + represented by a symbol. Supports exact symbolic arithmetic at compile time, and a perfect + conversion policy to `Quantity` types. + - **[`Unit`](./unit.md).** A type which represents a _unit of measure_. - **[`Magnitude`](./magnitude.md).** A special kind of compile-time number, which we use to