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

Я работаю над движком игры, и в настоящее время я застрял в разработке системы ввода-вывода. Я сделал это, сам движок не обрабатывает форматы любых, а позволяет пользователю реализовать все, что ему нужно, создав файл *.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);
};

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

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?

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

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

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

Ответ 1

Я знаю, что вы упомянули, что вы не используете С++ 11, но другие, рассматривающие этот вопрос, могут быть, поэтому здесь решение С++ 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

Ответ 3

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

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