- Notifications
You must be signed in to change notification settings - Fork139
a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies.
License
nholthaus/units
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A compile-time, header-only, dimensional analysis library built on c++14 with no dependencies.
If you are usingunits.h
in production code, I'd love to hear from you via GitHub issues!
- Various bug fixes and improvements
This version removes support for the Visual Studio 2013 compiler.
Features:
- units now include constexpr
name()
andabbreviation()
member functions, which do not really on string/iostream. - Builds with VS2017 Ninja generator out of the box
- string conversions are now locale aware
- added unary increment and decrement operators (
++
,--
), as well as unary+
operator.
Bug fixs:
- fixed compilation error when iostream was disabled
Features:
- 5x compile time improvement on MSVC.
- 1.5x compile time improvement on GCC.
- Even more dramatic reductions in compile time can be achieved if you opt-in to specific unit definitions instead of using all the library-defined types (which is the default value). Check outEnabling a subset of units to improve compilation time for instructions.
- Adds std::cout support for units with no defined abbreviation (they show up as a combination of SI base units)
- Support for
std::numeric_limits
of unit types. - Assignment operators for unit types:
-=
,+=
,/=
,*=
. - Added
min
andmax
overloads for units types inunits::math
. - Added
to_string
function andabbreviation
functions:auto len =3.5_m;auto str = units::length::to_string(len);auto abv = units::length::abbreviation(len);std::cout << str;// prints "3.5 m"std::cout << abv;// prints "m"
- Added units of data and data transfer:
bits
,bytes
,bits_per_second
, andbytes_per_second
. - Adds
value()
member for accessing underlying type. - Adds
value_type
trait, as a synonym forunderlying_type
. - Adds definitions for Julian and Gregorian years.
- Thanks to @dinocore1,
units
now supports cmake install andfind_packages
. From thepull request:# To have cmake install units library to a local 'install' directory:mkdirbuildcdbuildcmake -DCMAKE_INSTALL_PREFIX="install" ..cmake --build . --targetinstall# The units library can then be used in some other cmake project using# the standard 'find_package' command. Like so:find_package(units)
Bug fixes:
- Fixed singular name of
siemen
to besiemens
(Thanks @Oxyd) - Fixed bug with
cubrt
operation (Thanks @PearCoding) - Fixed constexpr relational operators bug
- Fixed exponential temperature conversions (Thanks @guarndt)
- gcc-4.9.3
- gcc-5.4.0
- clang-3.4
- msvc2015
- msvc2017
Does this library work on your compiler? If so, let me know!
- UNITS
- Get in touch
- Latest Release - v2.3.1
- Contents
- Documentation
- Description
- Getting started guide
- Unit initialization
- Unit tags
- Unit containers
- Unit Literals
<cmath>
Functions- Exponentials and Square Roots
- Removing type safety
- Efficiency
- Pure Compile-time Unit Manipulation
- Conversion without unit containers
- Namespaces
- Defining new units
- Unit definition macros
- Unit Type Traits
- Changing the underlying type of
unit_t
- Disabling IOStream
- Enabling a subset of units to improve compilation time
- Macro clashes
- CMake Instructions
- Build Instructions
- Previous Releases
The full documentation is availablehere.
The library consists of a single file (units.h), plus unit tests. To incorporate the library into your project, simply copy the header into a location in your include path, or add theincluded CMake project into your build. Using the CMake project, you can also build the unit tests and documentation if desired.
The library provides a set of types, containers, and traits to solve dimensional analysis problems, that is, problems involving dimensioned physical quantities. The conversions between units are defined as ratios at compile time, making the libraryincredibly fast. Additionally, specifying units astypes, rather than variable suffixes (or not at all), provides complete type-safety within the compiler. This means that code that accidentally misuses units or which has errors in the dimensional analysiswill fail at compile-time, not at run-time.
The unit test fileunitTests/main.cpp
contains example usage of every type, trait, and function contained in the library, and while not exactly user-friendly, can be a valuable resource.
Addunits.h
to your project, along with theusing
directive for literals
#include<units.h>usingnamespaceunits::literals;
Each "dimension" of unit is defined in its own namespace. Seethe namespaces section for a complete list. The rest of the guide assumes you've included the namespaces you plan to use:
usingnamespaceunits;usingnamespaceunits::length;usingnamespaceunits::time;usingnamespaceunits::area;usingnamespaceunits::velocity;
The easiest way to get started with theunits
library is to think of unit containers asdouble
values. Unit containers are typically the units' non-plural name with the suffix_t
(for type), e.g.meter_t
. Seethe documentation for a complete list.
Units can (and should!) be used anywheredouble
values can be used:
double area =15 *5 +10 *10;// 175 m^2?square_meter_t area = 15_m * 5_m + 10_m * 10_m;// 175 m^2
What makes unit types special is that unit conversions happen implicitly and automatically. Since unit conversions are evaluated at compile time, this means you can mix and match all the unit types you want withno runtime penalty.
foot_t len = 5_m;// simple implicit conversionmeters_per_second_t speed = 60_mi / 1_hr;// more complex implicit conversionsquare_meter_t area = 15_m * 5_m + 1000_cm * 1000_cm;// previous example with mixed units
Note the return type has the correct dimensions of area, even though the source types were all units of length.units.h
has powerful dimensional analysis capabilities. But what happens if we get the return type wrong?
meter_t area = 15_m * 5_m + 10_m * 10_m;// oops, m * m = m^2
E:/workspace/units/include/units.h(1405): error C2338: Units are not compatible.
Your compiler will produce an "incompatible units" error if your dimensional analysis is incorrect. If your resulting unit types are complex, you could useauto
for simplicity:
auto result = 15_m * 5_m + 10_m * 10_m;// m^2auto speed = 60_mi / 1_hr;// 60 mph
NOTE: Think carefully about usingauto
for return types. When you explicitly declare the return type, the compiler can check the dimensional analysis for correctness, and produce errors at compile time if you make a mistake. When usingauto
, you are basically saying that whatever unit the right-hand side of the expression results to is correct (even if it's not). If you are only usingauto
because a complex unit type is not available in the library, trydefining a new unit as a better alternative.
More complex mathematical operations (almost every<cmath>
operation actually), including exponentials and square roots are possibe by using theunits::math
namespace .
usingnamespaceunits::math;meter_t a = 3_m;meter_t b = 4_m;meter_t c = sqrt(pow<2>(a) + pow<2>(b));// Pythagorean threorem.std::cout << c << std::endl;// prints: "5 m"
There are several ways to initialize unit values:
Explicit initialization
meter_tdistance_m(10);// Explicit initialization from doublemeter_tdistance(10_m);// Explicit initialization from unit literalmeter_tdist(100_ft);// Explicit initialization from unit literal of a different type
make_unit<...>()
factory. The syntax is familiar toboost::units
users, and allows explicit reference to the unit type for member variable initialization.classmyClass{public:myClass() : m_speed(make_unit<miles_per_hour_t>(100)) {}private:miles_per_hour_t m_speed;};
Unit tags are the foundation of the unit library. Unit tags are types which are never instantiated in user code, but which provide the meta-information about different units, including how to convert between them, and how to determine their compatibility for conversion.
All unit tags are defined in namespaces under theunits
namespace, such asunits::length
orunits::angle
, to avoid name clashes between units of different physical quantities which share the same names (like pounds). SI base units are defined as "categories" in theunit
namespace.
Units are defined in terms of
- A scale factor relative to a base unit type.
- A base unit
- [optionally] a scale factor of
pi
- [optionally] a datum translation (such as the +/- 32 required to convert between
fahrenheit
andcelsius
)
All units have their origin in the Système International (SI) base unit system. A special exception is made for angle units, which are defined in SI as ( m * m^-1), and in this library they are treated as a basic unit type because of their important engineering applications.
Example: the definitions of some common length units are:
namespacelength{using meters = units::unit<std::ratio<1>, units::category::length_unit>;// meters are (1) unit of length in the SI system.using feet = units::unit<std::ratio<381,1250>, meters>;// feet are 0.3048 meters.}
Unit containers are the primary classes which will be instantiated in user code. They can be thought of as essentially equivalent to adouble
, except that they have unit type tags associated with them. They can be used wherever a double would be used to store a dimensioned quantity. Containers are derived from theunit_t
class, and have the form[unitname]_t
, e.g.meter_t
orradian_t
.
Unit containers are defined in terms of the units they represent, their underlying type, and an optional non-linear scale (think decibels or Richter scale). For example,meter_t
would be defined:
usingmeter_t = units::unit_t<units::length::meter,double, units::linear_scale>
or simply
usingmeter_t = units::unit_t<units::length::meter>
since the underlying type and scale parameters default todouble
andlinear_scale
respectively.
Units of compatible types (e.g length units) can be implicitly converted/assigned to one another. Units (with the exception of dimensionless types) cannot be implicitly converted to/from built-in types, such asdouble
.
Units are constructed from built-in types, and thetoDouble()
method (oroperator()
) can be used to retrieve a built-in type value. That said, the user should prefer to operate within the unit type-space as much as is practical, and wrappers of most<cmath>
functions are provided to enable operating solely in theunit_t
domain.
The primary purpose of unit containers is to provide type safety and dimensional analysis for mathematical operations. for instance, the velocity of an object can be calculated:
auto objectVelocity =meter_t(100.0) /second_t(2.0);
The resulting velocity type will be deduced to bevelocity::meters_per_second
with a value of 50.0. Additionally, if the return type if specified, the type system will verify that the units are compatible. For example, the following will fail to compile:
units::velocity::meters_per_second objectVelocity =square_meter_t(100.0) /second_t(2.0);// Error: Unit types are not compatible.`
Unit containers can (and should!) be used to perform implicit conversions:
units::time::second_t a;units::time::minute_tb(1.0);a = b;// a == 60.0
Arithmetic can be performed on unit containers the same way it can for built-in types. However, unlike built-in types, the return value of unit-type arithmetic will be the proper unit to represent the resulting quantity.
usingnamespaceunits::length;usingnamespaceunits::area;meter_ta_m(1.0), b_m(2.0), c_m;foot_ta_ft(1.0), b_ft(2.0), c_ft;c_m = a_m + b_m;// OK. c == 3mc_ft = a_m + b_m;// OK. resulting 3m is converted to ft.auto result = a_m + b_ft;// OK. result is `meter_t` (left-most unit)auto result_sm = a_m * b_m;// OK. result_sm is `square_meter_t`.auto result_s = a_m / b_m;// OK. result_s is `dimensionless_t`.auto result = a_m * b_ft;// OK. result is `square_meter_t` (left-most unit)auto result = a_m *square_meter_t(1.0);// OK. units can always be multiplied. Result is `cubed<meter_t>`.auto result = a_m *scalar_t(1.0);// OK. units can always be multiplied. Result is `meter_t`.
Unsupported arithmetic, or improper return types will result in compiler errors:
c_m = a_m +5.0;// Error. can't add scalars to dimensioned units.c_m = a_m +scalar_t(5.0);// Error. can't add scalars to dimensioned units.auto result = a_m +square_meter_t(1.0);// Error. Incompatible units.
By providing explicit return types for unit functions, the compiler can be used to verify the accuracy of the dimensional analysis, and thus avoiding costly errors.
If you are using a compiler which supports user-defined literals (e.g. not Visual Studio 2013), then unit literals can be a convenient way to initialize and work with unit values:
usingnamespaceunits::literals;meter_t dist= 10_m;// 10 mmeter_t dist2= 1_km;// 1000 m
Literals can also be used for any temporary values in calculations, making them more readable:
auto area = units::length::meter_t(5) *units::length::meter_t(10);// without literalsauto area = 5_m * 10_m;// with literals
All literals* are defined by their SI abbreviation preceded by an underscore, e.g._m
for meter. "Square" units are preceded by_sq
, e.g._sq_m
for square meters. Non SI units use their most common abbreviations.
All literals can be used with a metric prefix as table shows:
Metric Prefix | Literal | Example |
---|---|---|
femto | f | 10_fm |
pico | p | 10_pm |
nano | n | 10_nm |
micro | u | 10_um |
milli | m | 10_mm |
centi | c | 10_cm |
deci | d | 10_dm |
deca | da | 10_dam |
hecto | h | 10_hm |
kilo | k | 10_km |
mega | M | 10_Mm |
giga | G | 10_Gm |
tera | T | 10_Tm |
peta | P | 10_Pm |
All literals are defined in theunits::literals
namespace, and in order to use literals in your codeyou must include the lineusing units::literals
(since there is no way to put a namespace on an operator).
* with the exception ofTeslas
, which use_Te
for compatibility with MSVC compilers.
Theunits
library include type-safe unit_t container wrappers for almost all of the<cmath>
functions,including the c++11 extensions. These functions can be found in theunits::math
namespace. Theunits
library versions don't conflict with<cmath>
, and it's possible to use both libraries in the same code.
The overloaded functions ensure that only the proper unit types are accepted into the functions, and that the return value type matches the expected units, all without needing to result to the type-unsafetoDouble()
member.
Inrare cases, the overload resolution for a given type may be ambiguous. If so, simply prepend the function with the fully-qualifiedunits::math
prefix, e.g.
meter_tx(2.0);meter_ty(3.0);square_meter_tz(1.0);square_meter_t result;result = fma(x, y, z);// Error: ambiguousdouble result = fma(x.toDouble(), y.toDouble(), z.toDouble());// Warning: Unsafe!result = math::fma(x, y, z);// OK.
Many functions require units to be raised to some power. This can be accomplished using theunits::math::pow
function:
square_meter_t m2 = units::math::pow<2>(meter_t(5.0));// m2 == 25.0
The only constraint is that the exponential power (given in the template argument) must be known at compile time, so that the type system can deduce the output type. This differs from the<cmath> pow
implementation, which takes exponent values at runtime.
Square roots are also provided with theunits::math::sqrt
function. Due to the nature of thesqrt
operation, the units library can often provide exact conversions for square root operations, butnot in every case. The rest of the time, thesqrt
unit will be arational_approximation of the real value. These are guaranteed to be accurate to at least 10 decimal places.
meter_t m = units::math::sqrt(square_meter_t(4.0));// m == 2.0
When interfacing with APIs, libraries, and frameworks which aren'tunit
enabled, it may be necessary (if regrettable) to remove the type-safety of a unit container and expose its underlying type. This is possible using theunit_cast
function, or theto<>
member function.
usingnamespaceunits;usingnamespaceunits::length;// Get double value from a unit container (double is the default underlying type of the units library)meter_tdist(10);double dval = unit_cast<double>(dist);double dval2 = dist.to<double>();// Get integer value (potentially narrowing, be careful!)int ival = unit_cast<int>(dist);int ival2 = dist.to<int>();
Both functions produce the same results, the choice of syntax is simply a user preference.
To determine the underlying type of the unit container, the (verbose) traitunits::traits::unit_t_traits<decltype(dist)>::underlying_type
could be used.
Complex, recursively-defined conversions are performed in just 5 instructions:
year_t twoYears(2.0);week_t twoYearsInWeeks = twoYears;00007FF7BDB57FF6 xorps xmm9,xmm9 00007FF7BDB57FFA cvtsi2sd xmm9,rax 00007FF7BDB57FFF mulsd xmm9,mmword ptr [__real@4000000000000000 (07FF7BDBB31A0h)] 00007FF7BDB58008 divsd xmm9,mmword ptr [__real@401c000000000000 (07FF7BDBB33C0h)] 00007FF7BDB58011 movsd mmword ptr [rbp+6Fh],xmm9 EXPECT_EQ(week_t(104.286), twoYearsInWeeks);00007FF7BDB58017 ...
In the library, the year to week conversion is defined in terms ofyears -> days -> hours -> minutes -> seconds -> minutes -> hours -> days -> weeks
but the total conversion ratio is computed at compile-time and the math is optimized to two floating-point operations.
Unit conversions between equivalent types are optimized away completely, and generateno machine code.
In many cases, unit equations are used to determine derived values from a set of values which are known at compile-time. In these situations, it would be optimal to pre-compute the derived valuesat compile time, thus generating no machine code and incurring no run-time penalty.
Theunit_value_t
class is the mechanism in the units library to perform compile-time arithmetic. Theunit_value_t
class functions exactly the same way asstd::ratio
, but with an associated unit tag and the ensuing type safety.
For a simple example, let's define a right triangle whose hypotenuse is the sum of the squares of its side (a Pythagorean triple)
structRightTriangle{using a =unit_value_t<meters,3>;using b =unit_value_t<meters,4>;using c = unit_value_sqrt<unit_value_add<unit_value_power<a,2>, unit_value_power<b,2>>>;};
The definition above is perfectly efficient, as it generatesno run-time code whatsoever, and still provides all the type safety of unit containers. The values ofa
,b
, andc
can be accessed at runtime using the staticvalue()
method ofunit_value_t
auto a = RightTriangle::a::value();// a is `meter_t(3)`auto b = RightTriangle::b::value();// b is `meter_t(4)`auto c = RightTriangle::c::value();// c is `meter_t(5)`
The available compile-time operations are:
units::unit_value_add
units::unit_value_subtract
units::unit_value_multiply
units::unit_value_divide
units::unit_value_power
units::unit_value_sqrt
The preferred method of conversion is implicitly though the use of unit containers, however unit conversion can be accomplished usingunits::convert
for arithmetic types:
double val_in = convert<feet, inches>(1.0);// val_in == 12.0
For type-safe conversion, prefer implicit conversion via unit_t type containers..
Unit tags and containers are split into separate namespaces to avoid conflicting unit names which represent different physical quantities.
Unit tag andunit_t
container definitions are defined in the following namespaces:
- units::length
- units::mass
- units::time
- units::angle (plane)
- units::current
- units::temperature
- units::substance (amount of, i.e. moles)
- units::luminous_intensity
- units::solid_angle
- units::frequency
- units::velocity
- units::angular_velocity
- units::acceleration
- units::force
- units::pressure
- units::charge
- units::energy
- units::power
- units::voltage
- units::capacitance
- units::impedance
- units::magnetic_flux
- units::magnetic_field_strength
- units::inductance
- units::luminous_flux
- units::illuminance
- units::radiation
- units::torque
- units::area
- units::volume
- units::density
- units::concentration
- units::data
- units::data_transfer_rate
- units::constants (scalar and non-scalar physical constants like Avogadro's number)
Literal values for unit containers are defined in theliterals
namespace
- units::literals
Mathematical operations likesin
,log
,floor
, etc are defined in the following namespaces:
- units::math
Type traits that you can use to test unit types are defined in the following namespaces:
- units::traits
The units library strives to provide built-in types for every conceivable unit, and before defining your own units you should double-check the namespaces to make sure it's not already included. That said, if you need to roll your own units, the library is extensible by design.
Defining new units is simple, as they can be recursively defined as ratio of previously-defined units in a way that mimics natural language and is highly readable:
namespacetime{using seconds = units::unit<std::ratio<1>, units::category::time_unit>;using minutes = units::unit<std::ratio<60>, seconds>;using hours = units::unit<std::ratio<60>, minutes>;using days = units::unit<std::ratio<24>, hours>;using weeks = units::unit<std::ratio<7>, days>;using years = units::unit<std::ratio<365>, days>;}
Units are defined in the form:using [unit] = unit<std::ratio<[number of base units per unit]>, [base unit]>;
, where:
- the
[unit]
is what you are defining. - the
[base unit]
is the unit that[unit]
will be defined in terms of, and - the
[number of base units per unit]
is the conversion ratio between the two, expressed as astd::ratio
type.
Compound units are defined in a similar manner, with additional helper functions for polynomials:
using acceleration = compound_unit<meters, inverse<squared<seconds>>>;// (m / s^2)
The available helpers are:
units::inverse<...>
(inverts the unit, e.g. meters becomes meters^-1, or 1 / meters)units::squared<...>
(squares the unit, e.g. meters becomes meters^2)units::cubed<...>
(cubes the unit, e.g. meters becomes meters^3)units::square_root<...>
(takes the square root of the unit, e.g meters^2 becomes meters)units::atto<...>
throughunits::exa<...>
metric prefixes
Version2.1.0
of the units library simplifies the task of adding new units by introducing a set of macros for unit definitions:
UNIT_ADD(namespaceName, nameSingular, namePlural, abbreviation, definition)
This macro adds a single new unit to the given namespace, as well as a literal definition and
cout
support based on the givenabbreviation
. e.g.UNIT_ADD(length, foot, feet, ft, unit<std::ratio<381,1250>, meters>)
Would create the
units::length::feet
tag, theunits::length::foot_t
container type, and the_ft
literal.UNIT_ADD_WITH_METRIC_PREFIXES(namespaceName, nameSingular, namePlural, abbreviation, definition)
This macro has the same functionality as
UNIT_ADD
, but additionally adds unit types with all metric prefixes fromfemto
topeta
(smaller and larger prefixes mostly result in arithmetic overflow).UNIT_ADD_WITH_CUSTOM_TYPE(namespaceName, nameSingular, namePlural, abbreviation, underlyingType, definition)
This macro has the same functionality as
UNIT_ADD
, but additionally adds anunderlyingType
parameter, which can be used to create units with integral, or other underlying types. The library default underlying type isdouble
.UNIT_ADD_DECIBEL(namespaceName, nameSingular, abbreviation)
Adds the decibel representation for a previously-defined unit. e.g.
UNIT_ADD_DECIBEL(power, watt, dBW)
Adds the
dBW_t
container, and the_dBW
literal.UNIT_ADD_CATEGORY_TRAIT(unitCategory, baseUnit)
This macro creates a type-trait to check whether a unit is of a certain category, e.g. length. This is only necessary if defining new categories of units which are not included in
units.h
at all. e.g.UNIT_ADD_CATEGORY_TRAIT(length, meter)
Adds the
units::traits::is_length_unit
trait.
The units library provides a comprehensive set of type-traits, which can be used in templated user code to enforce that the unit types have certain properties.
For example, let's say you want to write a function that validates that the square footage of an office (given in any units), meets the minimum size required by local ordinance.
template<typename Units>boolisMinimumSize(Units x){return x >=square_feet_t(80.0);}
This function will fail to compile ifUnits
is not a unit of area (since incompatible unit types are not comparable), but it will produce a series difficult-to-understand template errors. Type traits could be used to make the error message more friendly:
template<typename Units>boolisMinimumSize(Units x){static_assert(units::traits::is_area_unit<Units>::value,"Input value x must represent an area quantity.");return x >=square_feet_t(80.0);}
See theunits::traits
namespace for a list of all the supported traits.
The default underlying type for all unit containers isdouble
. However, this can be overridden by providing a definition forUNIT_LIB_DEFAULT_TYPE
, e.g.
// Use 64-bit integers as the underlying unit type#defineUNIT_LIB_DEFAULT_TYPEint64_t#include<units.h>
NOTE: changing the underlying type may result in unexpected behavior. Unit conversion makes heavy use of division, which may make integral types unsuitable except for niche embedded applications. Using excessively large types may increase the number of arithmetic overflow errors.
For some embedded applications, it may bedesirable to remove all references to<iostream>
in order to reduce compiled binary size and RAM requirements. There are two ways to accomplish this:
If you are copy/pasting
units.h
into your project include directory, then simply defineUNIT_LIB_DISABLE_IOSTREAM
before including the header.#defineUNIT_LIB_DISABLE_IOSTREAM#include<units.h>
If you are including
units
in your project as aCMake
target (usingadd_subdirectory
), then all you need to do is set theDISABLE_IOSTREAM
cache option, either using the cmake-gui, or by adding the option to the cmake command line during configuration:cmake -DDISABLE_IOSTREAM=ON -DBUILD_TESTS=OFF ..cmake --build. --config Release
If you know that you only need a subset of the unit namespaces for your application, you can dramatically improve compilation time by disabling the default definitions, and then only opting-in to the namespaces you want. For example:
// Only use length and time#defineDISABLE_PREDEFINED_UNITS#defineENABLE_PREDEFINED_LENGTH_UNITS#defineENABLE_PREDEFINED_TIME_UNITS
The generic algorithm is
- disable the pre-defined units using
#define DISABLE_PREDEFINED_UNITS
- opt-in to the namespaces you want using
#define ENABLE_PREDEFINED_<namepsace name>_UNITS
Additionally, forCMake
users, there are equivalently-named cmake options defined which will automatically include the preprocessor definitions in your project. Alternatively, you can useadd_definitions()
in your cmake file to set macros globally::
// Only use length and timeadd_definitions(-DDISABLE_PREDEFINED_UNITS-DENABLE_PREDEFINED_LENGTH_UNITS-DENABLE_PREDEFINED_TIME_UNITS)
Be aware, some units depend on others. See the unit dependencies table bellow:
Unit | Dependencies |
---|---|
ACCELERATION | LENGTH, TIME |
ANGULAR_VELOCITY | ANGLE, TIME |
AREA | LENGTH |
DENSITY | MASS, VOLUME |
FORCE | ACCELERATION, LENGTH, MASS, TIME |
ILLUMINANCE | LENGTH, LUMINOUS_FLUX |
MAGNETIC_FIELD_STRENGTH | MAGNETIC_FLUX |
PRESSURE | FORCE, LENGTH |
RADIATION | ENERGY, MASS |
SOLID_ANGLE | ANGLE |
TORQUE | FORCE, LENGTH |
VELOCITY | LENGTH, TIME |
VOLUME | LENGTH |
With certain compilers, it is possible that system header files like<ctype.h>
will define macros which conflict with the unit literals, which use SI abbreviations. In these cases, it is general safe and advisable to#undef
the offending macros.
_T
is known to conflict, but is hardcoded into the compiler and can't be disabled. For this reason,Tesla
units use the_Te
abbreviation.
The following macros may need to be undefined on the Windows platform to useunits
:
#undef pascal#include<units.h>
The following macros may need to be undefined on the ARM platform to useunits::literals
:
#undef _U#undef _L#undef _N#undef _S#undef _P#undef _C#undef _X#undef _B#defineUNIT_LIB_DISABLE_IOSTREAM// it's prudent to disable IOStream on embedded platforms as well.#include<units.h>
It's best to undefine macros on an as-needed basis.
There are several ways to incorporateunits.h
into your project. The simplest is to just copyinclude/units.h
into your project include directory (which the licensing allows you to do). However, you'll have to properly set up the necessary compilation flags for C++14 (-std=c++14
on gcc).
However, if you are already using CMake as your build system, the recommended way to includeunits
is to copy the entireunits
project as a subdirectory within your own top-level project folder. Then, in your CMakeLists.txt file add
add_subdirectory(units)add_executable(${PROJECT_NAME} main.cpp)target_link_libraries(${PROJECT_NAME} units)
Also, if you are distributing headers that depends on units.h, you shoud consider using cmake'sfind_package
to check if the header is installed on the user's system:
```cmakefind_package(units)add_library(${PROJECT_NAME} my_lib.cpp)target_link_libraries(${PROJECT_NAME} units::units)```
The include path properties are part of theunits
target, so adding it as a subdirectory and linking against it is all you need to do, no need to worry about additional include directories.
If you don't care about the unit tests, you can minimize compile time by invoking CMake with the following option:
cmake -DBUILD_TESTS=OFF ..cmake -build.
The library itself consists of a single headerunits.h, and can be included into your project without being built.
The unit tests and documentation can be built with CMake. A doxygen installation is required to generate the documentation, and a Tex install is needed if pdf documentation is desired.
To build the tests:
- Ensure
cmake
is installed, and that thebin
directory is in your%PATH%
variable, and that a compiler likeVisual Studio 2015 Community Edition
is installed. - clone the repository or download the
.zip
package. - Open a
cmd
terminal and navigate to the source directory. - Type the following commands:
md build
cd build
cmake -Wno-dev ..
cmake --build . --config Release
- The tests will be created in an executable called
unitLibTest.exe
in the folderbuild/unitTests/Release
.
- Ensure you are using cmake 3.2 or later. You can verify this with
cmake --version
. - Ensure you are using gcc version 4.9 or greater. You can verify this with
gcc --version
. - clone the repository or download the
.tar.gz
package. - Open a terminal and navigate to the source directory.
- Type the following commands:
mkdir build
cd build
cmake -Wno-dev ..
cmake --build . --config Release
- The tests will be created in an executable called
unitLibTest
in the folderbuild/unitTests
.
v2.0.3
unit_t
types are now trivial types.unit_t
types support the unary minus (negation) operator.- Compile-time unit arithmetic via
unit_value_t
. - Unit-enabled ports of most
<cmath>
functions, including c++11 extensions. - Square-root manipulators for
unit
,unit_t
, andunit_value_t
. - Improved documentation.
v1.3.0
- Adds ostream support.
- bug fixes.
v1.2.2
- Bug fixes (#1) and namespace cleanup.
v1.2.0
- Adds angular velocity units.
v1.1.1
- Adds Doxygen and additional type traits.
v1.0.0
- Initial release.
About
a compile-time, header-only, dimensional analysis and unit conversion library built on c++14 with no dependencies.