Macro dimensioned::make_units
source · [−]macro_rules! make_units {
($System:ident;
$one:ident: $Unitless:ident;
base {
$($base:ident: $Unit:ident, $print_as:expr $(, $base_dim:ident)*;)+
}
derived {
$($derived_const:ident: $Derived:ident = ($($derived_rhs:tt)+) $(, $derived_dim:ident)*;)*
}
constants {
$($constant:ident: $ConstantUnit:ident = $constant_value:expr;)*
}
fmt = $to_fmt:ident;
) => { ... };
}
Expand description
Create a new unit system
This macro is the heart of this library and is used to create the unit systems with which it ships.
If you find yourself using this macro, please think about whether the unit system you are creating would be useful to others; if so, submit an issue to get it added to dimensioned.
Rather than try to parse the definition above, we will show an example of calling it, and then walk through it line by line.
#[macro_use]
extern crate dimensioned as dim;
pub mod ms {
make_units! {
MS;
ONE: Unitless;
base {
M: Meter, "m", Length;
S: Second, "s", Time;
}
derived {
MPS: MeterPerSecond = (Meter / Second), Velocity;
HZ: Hertz = (Unitless / Second), Frequency;
M3: Meter3 = (Meter * Meter * Meter), Volume;
M5: Meter5 = (Meter3 * Meter * Meter);
}
constants {
FT: Meter = 0.3048;
CM: Meter = CENTI * M.value_unsafe;
MIN: Second = 60.0;
HR: Second = 60.0 * MIN.value_unsafe;
PI: Unitless = consts::PI;
}
fmt = true;
}
pub use self::f64consts::*;
}
Okay, now let’s walk through it.
The macro performs some imports and defines quite a few things, so it’s strongly recommended to put it in its own module.
pub mod ms {
The first line after calling the macro is just the name we want to give the unit system followed by a semi-colon. This will be the name of the only type we define; all other type definitions are aliases to this type with different parameters.
make_units! {
MS;
The next line is the name of the constant and type alias we want for a dimensionless quantity in your system. That is, when all units have power of 0.
ONE: Unitless;
In the base
block, we define the base units for our system. Each line is of the format CONST: Type, "token", Dimension;
where CONST
is the constant we create, Type
is the type alias that
we’ll make for this unit, token
is what will show up when we print it, and Dimension
is
optional. If present, the macro will implement said dimension from the dimensions
module for this unit.
base {
M: Meter, "m", Length;
S: Second, "s", Time;
}
In the derived
block, we can make derived units from our base units. The beginning is similar; we
have CONST: Type
. After the equal signs, we have a formula to define this unit. The parentheses
are required, and the only things that can be inside them are the names of other units, and the *
and / operators. Hopefully, this part of the macro will be made more flexibile in the
future. Finally, we again end with an optional dimension. Note that there is none present for the
M5
line.
derived {
MPS: MeterPerSecond = (Meter / Second), Velocity;
HZ: Hertz = (Unitless / Second), Frequency;
M3: Meter3 = (Meter * Meter * Meter), Volume;
M5: Meter5 = (Meter3 * Meter * Meter);
}
In the constants
block, we can define constants of whatever values we wish. Note that the
constants in the base
and derived
blocks are always created with a value of 1.0.
All constants are created in both f32
and f64
flavors, in the submodules f32consts
and
f64consts
, respectively.
In addition, the modules for all integer constants are created. However, these only include
constants for base and derived units. The full list of integer modules is i8consts
, i16consts
,
i32consts
, i64consts
, isize_consts
, u8consts
, u16consts
, u32consts
, u64consts
,
usize_consts
.
If you would like non-unary integer constants, you will have to construct them yourself, like so
use dim::si;
const MIN: si::Second<u32> = si::Second::new(60);
Support for making this better is in the works.
In these submodules, the consts from the respective version of f32prefixes
or f64prefixes
are in
scope, hence the use of CENTI
in the CM
definition.
In addition, the respective version of core::f32::consts
or core::f64::consts
is in scope, which
allows the use of consts::PI
in the `PI definition.
constants {
FT: Meter = 0.3048;
CM: Meter = CENTI * M.value_unsafe;
MIN: Second = 60.0;
HR: Second = 60.0 * MIN.value_unsafe;
PI: Unitless = consts::PI;
}
Finally, we have the fmt
line. This line can either be fmt = true;
or fmt = false;
. In either
case, the trait core::fmt::Debug
is implemented for your unit system, but all of the other fmt
traits are implemented only if this is true. Setting it to false
allows you to have custom
printing for your system.
fmt = true;
}
This line isn’t part of the macro, but I wanted to include it as it is in all of the unit systems
defined in dimensioned. It lets us use the f64
flavor of constants much easier. E.g. we can now
type ms::M
instead of ms::f64consts::M
.
pub use self::f64consts::*;
}
And that’s it! The macro may seem complicated at first, but if you end up using it, I hope that it starts seeming intuitive fairly quickly.
In addition to creating a type, type aliases, and constants, this macro implements many traits for
your unit system, including (but not limited to) the traits in the traits
module and arithmetic operations.