Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish writing QuantityPoint reference #152

Merged
merged 6 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/discussion/idioms/unit-slots.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ 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{})`.

#### Notes for `QuantityPoint`

`QuantityPoint` doesn't use quantity makers: it uses quantity _point_ makers. For example, instead
of the quantity maker `meters`, it uses the quantity point maker `meters_pt`.

The implications are straightforward. If you have a `QuantityPoint` instead of a `Quantity`, then
use the quantity _point_ maker instead of the _quantity_ maker. The library will enforce this
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.

## Examples: rounding to RPM

Let's look at some examples, using this quantity variable:
Expand Down
120 changes: 57 additions & 63 deletions docs/reference/quantity.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,33 +41,10 @@ For more practice with this, see [Tutorial 102: API Types](../tutorial/102-api-t

## Constructing `Quantity` {#constructing}

There are several ways to construct a `Quantity` object. For concreteness, we'll use this skeleton
to show the signatures.
There are several ways to construct a `Quantity` object.

```cpp
template <typename Unit, typename Rep>
class Quantity {

// A) Implicit constructor from another Quantity
template <typename OtherUnit, typename OtherRep>
Quantity(Quantity<OtherUnit, OtherRep> other);

// B) Construct from `Zero`
Quantity(Zero);

// C) Default constructor
Quantity();

// D) Construct from equivalent type
template <typename T>
Quantity(T &&x);
// NOTE: only exists when `T` is an "exactly-equivalent" type. One example:
// `std::chrono::nanoseconds` can construct an `au::QuantityI64<Nano<Seconds>>`.
};
```

However, the _preferred_ way to construct a `Quantity` is actually none of these. It's the
_quantity maker_, which we describe next.
- The **preferred** way, which we'll explain below, is to use a _quantity maker_.
- The other ways are all normal C++ constructors.

### Quantity Maker (preferred)

Expand All @@ -84,17 +61,21 @@ Makers](../tutorial/101-quantity-makers.md).

### Implicit constructor from another `Quantity` {#implicit-from-quantity}

This is option `A)` from the [previous section](#constructing).

```cpp
template <typename OtherUnit, typename OtherRep>
Quantity<Unit, Rep>(Quantity<OtherUnit, OtherRep> other);
```

This constructor performs a unit conversion. The result will represent the _same quantity_ as the
input, but expressed in the _units_ of the newly constructed object's type. It will also convert
the stored value to the _rep_ of the constructed object, if necessary.

Here is the signature. We've simplified it slightly for illustration purposes, and enclosed it
inside the class definition for context.

```cpp
template <typename Unit, typename Rep>
class Quantity {
template <typename OtherUnit, typename OtherRep>
Quantity(Quantity<OtherUnit, OtherRep> other);
};
```

This constructor only exists when this unit-and-rep conversion is both meaningful and safe. It can
fail to exist in several ways.

Expand All @@ -119,14 +100,17 @@ constructor, and it must be "forced" to do so. See [`.as<T>(unit)`](#as) for mo

### Constructing from `Zero`

This is option `B)` from the [previous section](#constructing).
This constructs a `Quantity` with a value of 0.

Here is the signature, enclosed in the class definition for context.

```cpp
Quantity(Zero);
template <typename Unit, typename Rep>
class Quantity {
Quantity(Zero);
};
```

This constructs a `Quantity` with a value of 0.

!!! note
`Zero` is the name of the type. At the callsite, you would pass an _instance_ of that type,
such as the constant `ZERO`. For example:
Expand All @@ -135,29 +119,39 @@ This constructs a `Quantity` with a value of 0.
QuantityD<Meters> height{ZERO};
```

For more information on the motivation and use of this type, read [our `Zero`
discussion](../discussion/concepts/zero.md).

### Default constructor

This is option `C)` from the [previous section](#constructing).
Here is the signature of the constructor, enclosed in the class template definition for context.

```cpp
Quantity();
template <typename Unit, typename Rep>
class Quantity {
Quantity();
};
```

A default-constructed `Quantity` is initialized to some value, which helps avoid certain kinds of
memory safety bugs. However, the value is contractually unspecified. You can look up that value by
reading the source code, but we may change it in the future, and we would not consider this to be
a breaking change. The only valid operation on a default-constructed `Quantity` is to assign to it
later on.
memory safety bugs. However, **the value is contractually unspecified**. You can of course look up
that value by reading the source code, but we may change it in the future, and **we would not
consider this to be a breaking change**. The only valid operation on a default-constructed
`Quantity` is to assign to it later on.

### Constructing from corresponding quantity

This is option `D)` from the [previous section](#constructing).
Here is the signature. We've simplified it slightly for illustration purposes, and enclosed it
inside the class definition for context.

```cpp
template <typename T>
Quantity(T &&x);
// NOTE: only exists when `T` is an "exactly-equivalent" type. One example:
// `std::chrono::nanoseconds` can construct an `au::QuantityI64<Nano<Seconds>>`.
template <typename Unit, typename Rep>
class Quantity {
template <typename T>
Quantity(T &&x);
// NOTE: only exists when `T` is an "exactly-equivalent" type. One example:
// `std::chrono::nanoseconds` can construct an `au::QuantityI64<Nano<Seconds>>`.
};
```

This constructor will only exist when `T` has a ["corresponding
Expand All @@ -170,7 +164,7 @@ In order to access the raw numeric value stored inside of `Quantity`, you must e
unit at the callsite. There are two functions which can do this, depending on whether you want to
access by value or by reference.

### `.in(unit)` {#extracting-with-in}
### By value: `.in(unit)` {#extracting-with-in}

This function returns the underlying stored value, by value. See the [unit
slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`.
Expand All @@ -185,7 +179,7 @@ slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `uni
You can retrieve the underlying value by writing either `t.in(seconds)` (passing the
`QuantityMaker`), or `t.in(Seconds{})` (passing an instance of the unit).

### `.data_in(unit)`
### By reference: `.data_in(unit)`

This function returns a reference to the underlying stored value. See the [unit
slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `unit`.
Expand Down Expand Up @@ -235,14 +229,14 @@ are forbidden. Additionally, the `Rep` of the output is identical to the `Rep`

??? example "Example: forcing a conversion from inches to feet"
`inches(24).as(feet)` is not allowed. This conversion will divide the underlying value, `24`,
by `12`. While this particular value would produce an integer result, most other `int` values
would not. Because our result uses `int` for storage --- same as the input --- we forbid this.
by `12`. Now, it so happens that this _particular_ value _would_ produce an integer result.
However, the compiler must decide whether to permit this operation _at compile time_, which
means we don't yet know the value. Since most `int` values would _not_ produce integer results,
we forbid this.

`inches(24).as<int>(feet)` _is_ allowed. The "explicit rep" template parameter has "forcing"
semantics. This would produce `feet(2)`.

However, note that this operation uses integer division, which truncates: so, for example,
`inches(23).as<int>(feet)` would produce `feet(1)`.
semantics. This would produce `feet(2)`. However, note that this operation uses integer
division, which truncates: so, for example, `inches(23).as<int>(feet)` would produce `feet(1)`.

!!! tip
Prefer to **omit** the template argument if possible, because you will get more safety checks.
Expand All @@ -265,7 +259,7 @@ slots](../discussion/idioms/unit-slots.md) discussion for valid choices for `uni
`int`.

The above example used the quantity maker, `inches`. One could also use an instance of the unit
type `Inches`, writing `length.as(Inches{})`. The former is generally preferable; the latter is
type `Inches`, writing `length.in(Inches{})`. The former is generally preferable; the latter is
mainly useful in generic code where the unit type may be all you have.

**Without** a template argument, `.in(unit)` obeys the same safety checks as for the [implicit
Expand All @@ -280,14 +274,14 @@ are forbidden. Additionally, the `Rep` of the output is identical to the `Rep`

??? example "Example: forcing a conversion from inches to feet"
`inches(24).in(feet)` is not allowed. This conversion will divide the underlying value, `24`,
by `12`. While this particular value would produce an integer result, most other `int` values
would not. Because our result uses `int` --- same as the input's rep --- we forbid this.
by `12`. Now, it so happens that this _particular_ value _would_ produce an integer result.
However, the compiler must decide whether to permit this operation _at compile time_, which
means we don't yet know the value. Since most `int` values would _not_ produce integer results,
we forbid this.

`inches(24).in<int>(feet)` _is_ allowed. The "explicit rep" template parameter has "forcing"
semantics. This would produce `2`.

However, note that this operation uses integer division, which truncates: so, for example,
`inches(23).in<int>(feet)` would produce `1`.
semantics. This would produce `2`. However, note that this operation uses integer division,
which truncates: so, for example, `inches(23).in<int>(feet)` would produce `1`.

!!! tip
Prefer to **omit** the template argument if possible, because you will get more safety checks.
Expand Down
Loading
Loading