Единицы измерения в C++

я работаю над игровым движком, и в настоящее время я застрял в разработке системы ввода-вывода. Я сделал это так, что сам двигатель не справляется любой форматы файлов, но скорее позволяет пользователю реализовать все, что он хочет, создав *.dll файл с appriopriatly названными функциями внутри. Хотя это само по себе не было большой проблемой, моя главная забота-последствия, которые, вероятно, будут видны во время использования двигателя.

я создал простой resource интерфейс как базовый класс для всех вещей, которые пользователь может придумать, и я пытаюсь расширить его, сделав простые дочерние классы, посвященные общим типам данных, поэтому пользователю не нужно реализовывать основы самостоятельно (в настоящее время я думаю о audio, image, data и mesh). Начиная с audio класс, я суммировал своеобразную проблему, пытаясь решить, в каком типе я должен хранить информацию о частота. Обычный блок Герц, поэтому я решил сделать это unsigned int.

однако здесь есть небольшая проблема-что делать, если пользователь попытается установить его в килогерц? Предположим, что какой-то абстрактный формат файла может хранить его в обеих единицах на мгновение. Я сделал простой класс-оболочку, чтобы назвать тип устройства:

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

и решил позволить пользователю также использовать kHz:

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

в то время как функция внутри audio класс, который позволяет пользователю задать частота дискретизации объявляется как track& samplingRate(units::hertz rate);. Пользователь должен вызвать его, явно говоря, какой порядок величины он использует:

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

мой вопрос:

есть ли лучший способ заставить пользователя использовать блок измерения простым и элегантным способом? Может быть, шаблон дизайна или какое-то умное использование typedefs?

Также обратите внимание, что в процессе создания двигателя мне может потребоваться больше единиц, которые будут несовместимы с Герц. Из верхней части моей головы я хочу, чтобы пользователь мог установить цвет пикселя как делать units::rgb(123,42,120) и units::hsl(10,30,240).

я попытался найти жизнеспособный ответ и нашел только этот вопрос, но OP только хотел порядки величины без обеспечения того, что единицы не совместимы с другими.

Также обратите внимание, что я использую старый C++ версия, а не C++11. Хотя размещение решения, действительного в любой версии, отлично, это было бы приятно, если бы я мог также использовать его:)

3 ответов


Я знаю, что вы упомянули, что не используете C++11, но другие, кто смотрит на этот вопрос, могут быть, поэтому вот решение C++11 с использованием пользовательских литералов:

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;
}

выход:

44100Hz
44100Hz

библиотека Boost "Units" отлично подходит для этого типа вещей.

http://www.boost.org/doc/libs/1_55_0/doc/html/boost_units.html


можно использовать дизайн завод шаблон выполнить то, что вы ищете. Можно создать частотный класс с частным конструктором и несколькими статическими методами, которые будут создавать объект в зависимости от единиц, которые пользователь хочет использовать. Сохраняя конструктор частным, пользователь вынужден объявлять свои единицы явно, что снижает вероятность ошибки пользователя.

#include <iostream>

using namespace std;

class frequency
{
public:
  static frequency hertz(int hz)
  {
    return frequency(hz);
  }

  static frequency kilohertz(double kHz)
  {
    return frequency(kHz * KHZ_TO_HZ);
  }

  static frequency rpm(int rpm)
  {
    return frequency(rpm * RPM_TO_HZ);
  }

  int hz()
  {
    return m_hz;
  }

private:
  static const int KHZ_TO_HZ = 1000;
  static const int RPM_TO_HZ = 60;

  frequency(int hz) : m_hz(hz)
  {
  }

  int m_hz;
};

int main()
{
  wcout << frequency::hertz(44100).hz() << "Hz" << endl;
  wcout << frequency::kilohertz(44.100).hz() << "Hz" << endl;
}