Representation types (“Rep”)¶
The rep of a Quantity<U, R> is its second template parameter, R: the underlying raw numeric
type which holds the wrapped value. Most reps are arithmetic types such as double or int32_t,
but Au also supports other numeric types: std::complex, Eigen vectors and matrices, and other
“custom” reps.
Full documentation of the requirements for a valid rep is still in progress (see #52). This page documents the traits that Au provides for working with reps.
ScalarOf / ScalarOfTrait¶
ScalarOf<T> is the scalar type that the rep T is “based on”. For a scalar rep, this is simply
the rep itself. For a “compound” rep — a complex number, a vector, a matrix — it’s the type of
the underlying components.
The defining property of the scalar type is that it supports ordinary numeric comparisons, and has
a meaningful notion of “min” and “max” values. Au’s conversion
risk checks are built on these properties, so
ScalarOf<T> is what makes the overflow and truncation safety checks work for compound reps.
Syntax:
ScalarOf<T>is an alias fortypename ScalarOfTrait<T>::type.ScalarOfTrait<T>is the customization point: specialize it for types that Au can’t handle automatically.
Automatic detection¶
For most types, Au can detect the scalar type automatically, with no user action required. We apply a prioritized sequence of probes, where the first matching probe wins:
| Priority | Probe | Result | Convention served |
|---|---|---|---|
| 1 | std::is_arithmetic<T> |
T itself |
Arithmetic types |
| 2 | T::Scalar member |
T::Scalar |
Eigen |
| 3 | T::value_type member |
T::value_type |
STL containers |
| 4 | .real() member function |
decltype(t.real()) |
std::complex |
| 5 | (none matched) | No result; ScalarOf<T> does not exist |
(hard compiler error) |
Every probe tests an intrinsic property of T, so the answer is guaranteed to be the same in
every translation unit. This makes the automatic detection safe with respect to the One Definition
Rule (ODR).
Specializing ScalarOfTrait¶
If none of the probes match your type, you must specialize ScalarOfTrait for it. For example:
namespace au {
template <>
struct ScalarOfTrait<my::UnusualNumber> {
using type = double;
};
} // namespace au
Avoiding ODR violations
It is critical to ensure that ScalarOfTrait<T> has only one implementation for every type
T. This is called the “One Definition Rule” (ODR), and if you violate it, your program’s
behavior will be undefined.
To avoid this problem, go through these steps in order until you find the first one that matches your situation.
- First, if the default
ScalarOfTraitimplementation works (see “Automatic detection” above), then just use that. Never specializeScalarOfTrait<T>unless it gives a hard compiler error. - If it does give an error, that means you need the specialization.
ScalarOfTraitis highly greppable, so grep your codebase to see whether there’s an existing specialization for your specific type. If there is, then include that existing file. - If neither of the above worked, then create the specialization, and put it in a file that everyone can include.
Timeline¶
To provide a soft landing, ScalarOf support rolls out over two releases.
-
0.6.0 will be the first release that provides
ScalarOfTrait, but it will not yet be required (Au will fall back to the old machinery). -
0.7.0 will be the first release that requires
ScalarOfTraitto exist for every rep.
The upshot is that if you have a custom rep, and it is not covered under automatic detection, then
you should specialize ScalarOfTrait now, to avoid breakages when 0.7.0 is released.