Replies: 1 comment 8 replies
-
Here's the germ of an idea for fixing magnitudes: maybe the solution is more indirection? 😁 It might be nice if in the places where we use a magnitude, we could instead use an arbitrary type which had a trait which was a magnitude. Of course, we'd want to automatically generate these types for magnitudes that are the result of a (magnitude) computation. If we had a way to generate a human-readable compile-time string for every magnitude --- which, incidentally, is something we need to do anyway for generating unit lables --- then we could make that string a template parameter, so that it's what shows up in compiler errors. Something like (Can you do that in C++20? I think you can, but I spend basically all my time in pre-C++20 codebases.) I don't know if this will work, but it seems like a direction worth exploring. And I think it's more promising than just swapping out 5 and 10 as basis vectors, since that would only improve powers-of-10 --- a common use case, but there are many more. |
Beta Was this translation helpful? Give feedback.
-
The V2 Framework Rules
The below provides all the design rules for the V2 Framework.
Please provide feedback or start a discussion about controversial points in the new threads below.
0. TL;DR
Quantities of the same kind can be compared, added, and subtracted to/from each other:
The above behavior is mandated by ISO 80000.
Quantities of interconvertible references are implicitly convertible (copy-initialization works
as expected):
Copy- or direct-initialization of quantities of non-interconvertible references results in
a compile-time error.
1. Dimension
Dimension is an expression of the dependence of a quantity on the base quantities
of a system of quantities as a product of powers of factors corresponding to the base
quantities, omitting any numerical factor.
While defining a dimension of a new base quantity user needs to provide its unique
identifier. For example:
Dimensions can be multiplied and divided, or one can create an inverse of a dimension
with
1 / dimension
syntax. As a result of such a dimension equation we either geta dimension of a derived or base quantity. For example,
dim_length / dim_time
results in a dimension equal to the dimension of a derived quantity of
isq::speed
.However,
(dim_length / dim_time) * dim_time
results with thedim_length
again.Dimensions of derived quantities are constructed implicitly by the library's framework
as a result of quantity or dimension equation, and users should not define or instantiate
them directly. For example, both
dim_length / dim_time
andisq::length / isq::time
equations will implicitly create a derived dimension
LT⁻¹
, which is represented inthe library with the
derived_dimension<dim_length, per<dim_time>>
type.Dimension of a derived quantity is always defined in terms of dimensions of base
quantities, even if the quantity equation contains other derived quantities. For example,
isq::speed / isq::time
will create a quantity dimensionLT⁻²
which is representedin the library with the
derived_dimension<dim_length, per<power<dim_time, 2>>>
type.Dividing a dimension by itself results in dimension one represented by
dimension_one
type in the library.
Two dimensions are considered equal if they represent exactly the same dimension type.
For example, the dimensions of both
isq::energy
andisq::moment_of_force
quantities areequal as both quantities have a dimension of
L²MT⁻²
, which is represented in the libraryby the
derived_dimension<power<dim_length, 2>, dim_mass, per<power<dim_time, 2>>>
type.2. Quantity Specification
Quantity specification provides all domain-specific properties of a quantity beside
its unit (i.e. derived quantity equation, kind, dimension, character, point offset, ...).
This means that quantity specification is equivalent to a quantity definition in
a system of quantities (e.g. International System of Quantities ISQ).
Base quantity specification definition takes its unique dimension as a template parameter.
For example:
Some quantities definitions can base on other quantities. Such definitions form
a hierarchy of quantities.
More than one quantity can base on the same specific quantity, which creates
branches in the hierarchy. For example, the below defines quantities
of
width
andheight
which are both based on thelength
quantity. Such definitionsstart two separate branches in the hierarchy of quantities:
Derived quantities are formed as a result of the quantity equation. The quantity
equation supports the same operations as the dimension equation. The important
difference in behavior here is that the type of a derived quantity preserves
exactly the types used in the equation (does not break them into base quantities).
For example, an equation of
isq::speed / isq::time
results with aderived_quantity_spec<isq::speed, per<isq::time>>
type.Only if a numerator and denominator contain exactly the same quantity
type such quantities are simplified (removed from the equation). For example,
isq::speed * isq::force / isq::speed
results in the quantity ofisq::force
.If a quantity specification is divided by itself, a result will be a quantity of
dimension one (also known as a dimensionless quantity). For example,
isq::speed / isq::speed
results in adimensionless
quantity specification.However, the
(isq::length / isq::time) / isq::speed
results in aderived_quantity_spec<isq::length, per<isq::speed, isq::time>>
type asthe operands were not exactly of the same type.
Two quantity specifications are considered equal if they represent exactly
the same quantity type. For example,
isq::length / isq::time
is differentthan
isq::speed
.Providing a compatible unit to a quantity specification's
operator[]
results ina reference. For example,
isq::energy[J]
results in thereference<isq::energy, si::joule>{}
.2.1. Quantity kinds
Quantities of the same kind within a given system of quantities have the same quantity
dimension. However, quantities of the same dimension are not necessarily of the same kind.
The division of the concept "quantity" into several kinds is to some extent arbitrary.
In mp-units, each base quantity automatically becomes a distinct kind for the hierarchy of
quantities based on it. For example, a quantity of
isq::length
is ofisq::length
kind,and
isq::time
quantity is of anisq::time
kind.Quantities based on base quantities are of a base quantity kind. For example, both
quantities of
isq::width
andisq::height
are of aisq::length
kind.Derived quantity kinds are formed from the kind of their bases. For example, quantities
of
isq::speed
,isq::length / isq::time_period
, andisq::height / isq::time
are of a
isq::length / isq::time
kind.User can introduce more fine-grained kinds in a hierarchy with additional
kind_of(quantity_spec)
attribute while defining a new quantity. For example:The above assigns explicitly a
frequency
kind to its quantity. Such quantity is no longera quantity of
1 / time
kind. In case there are other quantities that base on afrequency
quantity, they will automatically derive itsfrequency
kind as well.Similar is true for
kinetic_energy
, which is of anenergy
kind even though itsdefinition does not derive from it.
2.2. Common quantity
A common quantity of two quantities is the first shared quantity type in their hierarchy
of quantity types. For example, a common quantity of
isq::width
andisq::height
is
isq::length
.In case two quantities do not have shared quantities in their hierarchies and they
are of the same kind, such kind quantity becomes their common quantity. For example,
kinetic_energy
andpotential_energy
are both of anenergy
kind, even thoughtheir definitions do not explicitly derive from a quantity of
energy
.Otherwise, two quantities do not have a common quantity type.
2.3. Interconvertible quantities
Two quantities are interconvertible if they belong to the same logical branch in
the quantities hierarchy.
Quantities of different dimensions are not interconvertible.
If two quantities have a common base and one quantity definition derives from another
one, then such two quantities are considered interconvertible.
If two derived quantities are of the same dimension, but they do not have a common
base, then the ingredients of both quantities are checked for interconvertibility
(TODO Interconvertibility of derived quantities #427).
3. Unit
Units provide all unit-specific information
(e.g. symbol, prefix, factor, derived unit equation, associated
quantity_kind
, ...)Base units may be defined in terms of a base quantity specification. For example:
Derived units are formed as a result of the unit equation. Unit equation supports the
same operations as quantity equation. This means that the types of ingredients are preserved
in the final equation. For example,
J / m
will result with aderived_unit<si::joule, per<si::metre>>
type.
Dividing a unit by itself results in unit one represented by
one
type in the library.Derived units can have their names and symbols as well. For example:
Similarly to base units, derived unit definitions can explicitly specify a quantity kind
they are limited to work with. For example:
If all units in a unit equation have associated quantity specifications, then
a resulting unit will also have an associated quantity specification being the result of
an equivalent quantity equation on corresponding quantities.
Two units compare equal (are equivalent) if they represent the same canonical unit which
may have various forms. For example, all of the
kg⋅m²⋅s⁻²
,N⋅m
,Pa⋅m³
,W⋅s
, andC⋅V
represent the same unit
J
(joule).Two units are interconvertible if they are (possibly scaled/prefixed) versions
of the same unit. For example,
kJ
andN⋅cm
are interconvertible but not equivalent.A common unit of two interconvertible units is a unit expressed in terms of base units with
such a scaling factor that any of the quantities will not truncate its value. For example,
a common unit of
m / s
andkm / h
is[1/18] m/s
represented in the library witha
scaled_unit<mag<ratio{1, 18}>, derived_unit<si::metre, per<si::second>>>
type.TODO Try to refactor the unit magnitude support to provide better types at compile time
as the above generates for now
scaled_unit<magnitude<power_v<2, -1>(), power_v<3, -2>()>(), derived_unit<si::metre, per<si::second>>>
A number multiplied by a unit with associated quantity specification forms a quantity.
4. Reference
Reference provides all domain-specific properties of the quantity.
Reference
concept is satisfied with either an instance of aunit
with an associatedquantity specification or specialization of a
reference<quantity_spec, unit>
.If the
unit
in a specific system of units has an associatedquantity_spec
(either explicitly by providing
quantity_spec
in unit definition or transitivelyas a derived unit of such units) it is the simplest way to specify a quantity reference.
In such case, a
quantity_spec
of such quantity will be derived from the unit equation.For example,
42 * m / (2 * s)
will result in a quantity that will have unnamedquantity_spec
ofderived_quantity_spec<isq::length, per<isq::time>>
(not a named
isq::speed
).In case we want to use a narrower quantity specification than the one specified
by a unit (e.g. of a specific quantity type or kind), we should create
a reference by attaching a unit to an
operator[]
of a quantity specification. For example,isq::altitude[m]
will result inref<isq::altitude, si::metre>
.Two references compare equal (are equivalent) if their quantity specifications and units
are equivalent as well.
Two references are interconvertible if their quantity specifications and units are interconvertible
as well.
A common reference of two references is a reference of a common quantity specification and
common unit.
A number multiplied by a reference forms a quantity.
5. Quantity
quantity
is defined in terms of a number and a reference.quantity
instance provides its number, reference, quantity specification, dimension, and unit.quantity
is created either byquantity<si::hertz, int>{42}
orquantity<isq::altitude[si::metre]>{42}
,42 * Hz
or42 * isq::altitude[si::metre]
.5.1. Quantity conversions
quantity
instance can be converted to anotherquantity
type.To change a quantity specification, we can provide an existing quantity instance to an
operator()
of the target quantity specification, provided that both specifications are interconvertible.
For example,
isq::speed(10 * (m / s))
orisq::altitude(42 * m)
.If specifications are not interconvertible, such an expression is invalid.
For example,
isq::altitude(42 * isq::radius[m])
should not compile.To change a unit, we can call
operator[]
of the quantity with a new interconvertible unit.This is allowed only if the stored number will not be truncated (the number is either of
a floating-point type or the unit conversion ratio is integral).
For example:
In case of non-interconvertible units or possible truncation, the code should not compile.
Quantity converting constructors participate in overload resolution only for interconvertible
references and only if during conversion the stored number will not be truncated
(target number is of a floating-point type, or either source target number is not of
a floating-point type or the unit conversion ratio is integral).
Quantities of interconvertible references ARE implicitly upcastable. For example:
void foo(quantity<isq::length[m]> a)
can be called withfoo(isq::radius(42 * m))
Quantities of interconvertible references ARE implicitly downcastable. For example:
void foo(quantity_of<isq::width> auto a, quantity_of<isq::height> auto b)
can be called withfoo(42 * m, 42 * m)
void foo(quantity<isq::kinetic_energy[J]> e)
can be called withfoo(40 * J)
.void foo(quantity_of<isq::speed> auto s)
can be called withfoo(42 * m / (2 * s))
void foo(quantity<isq::frequency[Hz]> f)
can be called withfoo(40 * (1 / s))
quantity<isq::speed[m/s]> q = 42 * m / (2 * s);
is validstd::array<quantity<isq::kinetic_energy[J]>, 3> arr = { 5 * J, 10 * J, 15 * J };
is validQuantities of non-interconvertible references are NOT convertible. For example:
void foo(quantity_of<isq::width> auto a, quantity_of<isq::height> auto b)
will triggera compile-time error when called with
foo(123 * isq::height[m], 42 * isq::width[m])
.void foo(quantity<isq::kinetic_energy[J]> e)
will trigger a compile-time error when calledwith
foo(isq::potential_energy(40 * J))
.5.2. Casting of a quantity
truncation, a dedicated
representation_cast<>()
should be used. For examplerepresentation_cast<int>(3.14 * m)
representation_cast<km>(1024 * m)
representation_cast<isq::radius[km]>(1024 * m)
representation_cast<quantity<isq::radius[km], int>>(3.14 * m)
5.3. Quantity comparisons
Quantities of the same kind ARE comparable. For example,
isq::radius(42 * m) == isq::height(42 * m)
.Quantities of different kinds (even with the same dimension) are NOT comparable.
For example, assuming that
frequency
andactivity
are specified as separate quantity kinds, bothisq::frequency(42 * Hz) == isq::activity(42 * Bq)
and42 * isq::frequency[1 / s] == 42 * (1 / s)
should not compile.5.4. Quantity arithmetics
Quantities of the same kind CAN be added or subtracted from each other, and the result of
such operation is the first common quantity specification type. For example,
2 * m + isq::radius(2 * m) + isq::altitude(2 * m)
results with6 * m
,isq::kinetic_energy(42 * J) + isq::potential_energy(42 * J)
results withisq::energy(84 * J)
, andisq::speed(10 * (m / s)) + 20 * m / (2 * s)
results with20 * (isq::length / isq::time)[m / s]
(not quantity of speed).Quantities of different kinds (even with the same dimension) can NOT be added or subtracted.
For example, assuming that
frequency
andactivity
are specified as separate quantity kinds, all ofisq::frequency(42 * Hz) + isq::activity(42 * Bq)
,42 * isq::frequency[1 / s] + 42 * (1 / s)
, and42 * isq::frequency[1 / s] + 42 * (1 / s) + 42 * isq::activity[1 / s]
should not compile.Beta Was this translation helpful? Give feedback.
All reactions