I am currently designing an API where I want that the user to be able to write code like this:
PowerMeter.forceVoltage(1 mV);
PowerMeter.settlingTime(1 ms);
Take a look at Boost.Units. Here's some example code:
quantity<energy>
work(const quantity<force>& F, const quantity<length>& dx)
{
return F * dx; // Defines the relation: work = force * distance.
}
...
/// Test calculation of work.
quantity<force> F(2.0 * newton); // Define a quantity of force.
quantity<length> dx(2.0 * meter); // and a distance,
quantity<energy> E(work(F,dx)); // and calculate the work done.
I prefer avoiding macros where ever I can, and this is an example where it should be possible. One lightweight solution that gives you correct dimensions would be:
static double m = 1;
static double cm = 0.1;
static double mV = 0.001;
double distance = 10*m + 10*cm;
This also reflects the physical concept that units are something that's multiplied with the value.
You could use C++11's compile-time rational arithmetic support for the units, instead of defining literals or macros for the units.
Consider using an enum
for your units and pass it as a second parameter:
namespace Units
{
enum Voltage
{
millivolts = -3,
volts = 0,
kilovolts = 3
};
enum Time
{
microseconds = -6,
milliseconds = -3,
seconds = 0
};
}
class PowerMeter
{
public:
void forceVoltage(float baseValue, Units::Voltage unit)
{
float value = baseValue * std::pow(10, unit);
std::cout << "Voltage forced to " << value << " Volts\n";
}
void settlingTime(float baseValue, Units::Time unit)
{
float value = baseValue * std::pow(10, unit);
std::cout << "Settling time set to " << value << " seconds\n";
}
}
int main()
{
using namespace Units;
PowerMeter meter;
meter.settlingTime(1.2, seconds);
meter.forceVoltage(666, kilovolts);
meter.forceVoltage(3.4, milliseconds); // Compiler Error
}
Wrapping the Units
namespace around the enums avoids polluting the global namespace with the unit names. Using enums in this way also enforces at compile time that the proper physical unit is passed to the member functions.
I prefer the solution from Anders K, however you may use a template to save some time implementing all units as a separte class which can be timeconsuming and prone to errors as you may need to write a lot of code by hand:
enum Unit {
MILI_VOLT = -3,
VOLT = 0,
KILO_VOLT = 3
};
class PowerMeter
{
public:
template<int N>
void ForceVoltage(double val)
{
std::cout << val * pow(10.0, N) << endl;
};
};
Use like this:
PowerMeter pm;
pm.ForceVoltage<MILI_VOLT>(1);
pm.ForceVoltage<VOLT>(1);
pm.ForceVoltage<KILO_VOLT>(1);
Before you go crazy with anything more complicated, whenever you write new code that takes a quantity as an argument you should name your methods like this so that it's 100% clear:
PowerMeter.forceInMilliVolts( ... )
PowerMeter.settlingTimeInSeconds( ... )
And similarly use variables with the right names e.g.:
int seconds(10);
int milliVolts(100);
This way it does not matter if you have to convert, it is still clear what you are doing e.g.
PowerMeter.settlingTimeInSeconds( minutes*60 );
When you are ready with something more powerful move to that, if you really need to, but make sure you do not lose the clarity of which unit is being used.