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

Support non-arithmetic Rep #52

Open
chiphogg opened this issue Dec 22, 2022 · 1 comment
Open

Support non-arithmetic Rep #52

chiphogg opened this issue Dec 22, 2022 · 1 comment
Labels
⬇️ affects: code (implementation) Affects implementation details of the code 📁 kind: enhancement New feature or request 💪 effort: medium

Comments

@chiphogg
Copy link
Contributor

As mentioned in #38, it's not our intention to restrict Rep to built-in arithmetic types forever. This issue tracks the idea of defining a separate trait which specifies exactly which properties are needed for a valid Rep.

Note that as part of this effort, we would need to check the impact on compile times.

@chiphogg
Copy link
Contributor Author

To be clear: we already have experimental support for non-is_arithmetic Rep. Apparently, people have been using this, and it's been working fine so far. However, it would be nice to add constraints that specify exactly what a type needs in order to be a valid Rep. This could give more direct error messages for end users.

@chiphogg chiphogg added ⬇️ affects: code (implementation) Affects implementation details of the code 💪 effort: medium labels Mar 26, 2023
SagaciousB pushed a commit to SagaciousB/au that referenced this issue Nov 22, 2023
Constants (aurora-opensource#90) and symbols (aurora-opensource#43) have a lot of similarities.  For
example:

- Multiplying by a raw number produces a quantity.
- Multiplying by a quantity changes the units of the quantity.
- They can compose with other instances of the same family (e.g., the
  product of two constants is a constant).

Let's use the term "wrapper" to refer to constants and symbols here. To
make each wrapper as easy as possible to implement, I've created some
"mixin" classes, each of which adds the full set of multiplication and
division operations for a single combination of the wrapper and some
other family of types (raw numbers, quantities, other wrappers, etc.).

These wrappers use a "CRTP-ish" syntax.  But instead of the _type
itself_ being the first template parameter (e.g., `Wrapper<Unit>`), we
provide the _wrapper_ and _unit_ as two separate parameters (i.e.,
`Wrapper` and `Unit`).  The reason is that we need to know the `Unit`,
and providing it explicitly makes it easy to get.  These first two
parameters are the same for all mixins, which makes the list-of-mixins
at the class definition easier to read.

The unit tests are based on an example, `UnitWrapper`, which aggregates
a variety of properties.  The forthcoming "true" classes, `Constant` and
`SymbolFor`, will be defined very similarly to this.

Finally, a word about the plan for "numeric" inputs.  For now, we are
restricting to `std::is_arithmetic`.  This doesn't mean we don't support
other reps; it just means we won't be able to create quantities from
them via constants or symbols for a while.  The evolution plan is to
create a well-defined concept that defines what is a valid rep, and then
replace `std::arithmetic` with that concept (see also aurora-opensource#52).

Test plan:

- [x] Add extensive new unit tests
- [x] Manually uncomment each individual "uncomment to test" case
chiphogg added a commit that referenced this issue Mar 30, 2024
The goal of these `std::is_arithmetic` SFINAE uses was to avoid
ambiguous overloads when we multiply a `Quantity` with another
`Quantity`.  It was only ever a shortcut.  It seems to work well in most
cases, but recently, a user pointed out that it doesn't help
`std::complex`.  (To be clear, we could _form_ a `Quantity` with
`std::complex` rep, but we couldn't multiply or divide a `Quantity` with
a `std::complex` scalar.)

This PR takes a different approach.

First, we define what constitutes a valid "rep".  We're very permissive
here, using a "deny-list" approach that filters out empty types, known
Au types, and other libraries' units types (which we detect via our
`CorrespondingQuantity` trait).  Incidentally, this should give us a
nice head start on #52.

Next, we permit an operation (multiplication or division) depending on
how the "candidate scalar" type would interact with the Quantity's rep:
simply put, the result must itself both exist, and be a valid rep.

The net result should be that we automatically consider a much wider
variety of types --- including complex number types from outside the
standard library --- to be valid scalars.  Crucially, this should also
be able to avoid breaking existing code: we are introducing potentially
a _lot_ of new overloads for these operators, and we can't allow any to
create an ambiguity with existing overloads.
chiphogg added a commit that referenced this issue Apr 25, 2024
The goal of these `std::is_arithmetic` SFINAE uses was to avoid
ambiguous overloads when we multiply a `Quantity` with another
`Quantity`.  It was only ever a shortcut.  It seems to work well in most
cases, but recently, a user pointed out that it doesn't help
`std::complex`.  (To be clear, we could _form_ a `Quantity` with
`std::complex` rep, but we couldn't multiply or divide a `Quantity` with
a `std::complex` scalar.)

This PR takes a different approach.

First, we define what constitutes a valid "rep".  We're very permissive
here, using a "deny-list" approach that filters out empty types, known
Au types, and other libraries' units types (which we detect via our
`CorrespondingQuantity` trait).  Incidentally, this should give us a
nice head start on #52.

Next, we permit an operation (multiplication or division) depending on
how the "candidate scalar" type would interact with the Quantity's rep:
simply put, the result must itself both exist, and be a valid rep.

The net result should be that we automatically consider a much wider
variety of types --- including complex number types from outside the
standard library --- to be valid scalars.  Crucially, this should also
be able to avoid breaking existing code: we are introducing potentially
a _lot_ of new overloads for these operators, and we can't allow any to
create an ambiguity with existing overloads.

Helps #228, but we need more Magnitude support.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⬇️ affects: code (implementation) Affects implementation details of the code 📁 kind: enhancement New feature or request 💪 effort: medium
Projects
None yet
Development

No branches or pull requests

1 participant