Units of measurement in C++

Paweł Stawarz picture Paweł Stawarz · Feb 19, 2014 · Viewed 11.9k times · Source

I'm working on a game engine, and currently I'm stuck designing the IO system. I've made it so, the engine itself doesn't handle any file formats, but rather lets the user implement anything he wants by creating a *.dll file with appropriately named functions inside. While that itself wasn't much of a problem, my main concerns are the implications that'll probably be visible during the usage of the engine.

I designed a simple resource interface as a base class for all the things the user can think of, and I'm trying to extend it by making simple child classes dedicated to the common data types, so the user doesn't have to implement the basics by himself (currently I'm thinking of audio, image, data and mesh). Starting with the audio class, I've stumbled on a peculiar problem, while trying to decide in what type should I store information about sampling rate. The usual unit are hertz, so I decided to make it an unsigned int.

However there's a little problem here - what if the user tries to set it in kilohertz? Let's assume some abstract file format can store it in both units for a moment. I've made a simple wrapper class to name the unit type:

class hertz{
private:
    unsigned int value;
    hertz(){};
public:
    operator unsigned int();
    hertz(unsigned int value);
};

and decided to let the user also use kHz:

class kilohertz{
private:
    float value;
    kilohertz(){};
public:
    operator hertz();
    kilohertz(float value);
};

While the function inside the audio class, which lets the user set the sampling rate is declared as track& samplingRate(units::hertz rate);. The user has to call it by explicitly saying what order of magnitude he's using:

someAudioFile.samplingRate(hertz(44100));
someAudioFile.samplingRate(kilohertz(44.1));

My question is:

Is there a better way to force the user to use a measurement unit in a simple and elegant way? A design pattern maybe, or some clever use of typedefs?

Please also note that in the process of creating the engine, I may require more units which will be incompatible with Hertz. From the top of my head - I may want the user to be able to set a pixel color both by doing units::rgb(123,42,120) and units::hsl(10,30,240).

I've tried searching for a viable answer and only found this question, but the OP only wanted orders of magnitude without ensuring the units are not compatible with other ones.

Also please note I'm using the old C++ version, not C++11. While posting a solution valid in any version is great, it would be nice if I could also use it :)

Answer

congusbongus picture congusbongus · Feb 19, 2014

I know you mentioned you aren't using C++11 but others looking at this question may be, so here's the C++11 solution using user defined literals:

http://ideone.com/UzeafE

#include <iostream>
using namespace std;

class Frequency
{
public:
    void Print() const { cout << hertz << "Hz\n"; }

    explicit constexpr Frequency(unsigned int h) : hertz(h) {}
private:
    unsigned int hertz;
};
constexpr Frequency operator"" _Hz(unsigned long long hz)
{
    return Frequency{hz};
}
constexpr Frequency operator"" _kHz(long double khz)
{
    return Frequency{khz * 1000};
}

int main()
{
    Frequency(44100_Hz).Print();
    Frequency(44.1_kHz).Print();
    return 0;
}

Output:

44100Hz
44100Hz