1
\$\begingroup\$

Since I use C++ mainly to do physical calculations, I often come across the problem that I forget which units a certain variable is in, e.g. radian or degree, or meter vs. millimeter.

For this reason, I started using a custom header file defining my units, which is posted below. The basic idea is that I never create something like:

double myAngle = 10;

Instead of using:

radian_t myAngle = 10;

Later, when passing it somewhere else, nobody will know whether it is in radian or degree. The upsides are listed below.

Are there any downsides to this?

Example implementation:

units.h:

#ifndef INCLUDE_UNITS_H_
#define INCLUDE_UNITS_H_

namespace units {
  using meter_t  = double;
  using newton_t = double;
  using radian_t = double;
  using joule_t  = double;

} // units

#endif /* INCLUDE_UNITS_H_ */

sample.h:

using namespace units;

joule_t myEnergy (newton_t force, meter_t length)
{ joule_t myEnergy = force * length; } 

The upsides are:

  1. Fast unit lookup: In Eclipse, I only need to hover over a variable and instantly know the unit it is in, simple by seeing its type (meter_t myLength vs double myLength).

  2. Easy debugging: The following code is obviously wrong, since Joule is Newton * meter:

    joule_t myEnergy (newton_t force, millimeter_t length)
    { joule_t myEnergy = force * length; } 
    

    Is this function correct? Who knows!

    double myEnergy (double force, double length)
    { double myEnergy = force * length; }
    
  3. Overloading? In theory, I could even implement classes instead of using "using" declarations. Is that correct? This would let me overload the operators, making sure that sin.operator*(radian_t rad) is different from sin.operator*(degree_t deg)?

\$\endgroup\$
  • 1
    \$\begingroup\$ Are you aware of Boost.Units? \$\endgroup\$ – Null Mar 27 '16 at 19:06
  • \$\begingroup\$ I wasn't, thanks for mentioning it! This looks like what I was looking for. Sadly, my current project may only use the C++ Standard library, so Boost is out for now. \$\endgroup\$ – Thomas Mar 27 '16 at 22:38
3
\$\begingroup\$

My question is, are there any downsides to this?

There's a huge downside. One is that the compiler does not enforce your units. Your type names are aliases. You could pass a variable of type newton_t to a function expecting a meter_t. The only way to detect this error is via inspection. Your use of units give a false sense of security.

Another is that it invites you to create a plethora of units such as millimeter_t, etc. This will get you in trouble. My advice: Except for input and output, stick to strict SI units. No millimeters or kilometers, no minutes or days, no grams or tonnes. Lengths internally are always in meters, time durations are always in seconds, masses are always in kilograms (except for star and planet masses, which are expressed in meters3/seconds2; see https://en.wikipedia.org/wiki/Standard_gravitational_parameter).

Another way to look at it is to look at Wolfram Alpha. Dozens, if not hundreds, of human years of development effort went into this product -- and it still messes up with units. You are not going to replicate that effort. Using consistent units throughout is the easiest way to confront the units problem.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer! Asides from the "false sense of security", are there any downsides compared to using "double" for all units? I definitely plan on sticking to SI units, but having a reminder of what is supposed to go into a function seems like an additional benefit, even if there is no error checking... \$\endgroup\$ – Thomas Mar 27 '16 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.