Единицы измерения в С++
Я работаю над движком игры, и в настоящее время я застрял в разработке системы ввода-вывода. Я сделал это, сам движок не обрабатывает форматы любых, а позволяет пользователю реализовать все, что ему нужно, создав файл *.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
Ответ 2
Библиотека Boost "Units" отлично подходит для такого типа вещей.
http://www.boost.org/doc/libs/1_55_0/doc/html/boost_units.html
Ответ 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;
}