2014-02-19 6 views
12

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

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

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

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).

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

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

+1

Не можете ли вы просто правильно документировать свой API? –

+1

Да, во избежание путаницы, возможно, у вас должен быть свой API-интерфейс на один стандарт, герц или килогерц, не оба. – leetNightshade

+0

Документирование моего API ничего не изменит, так как я хочу, чтобы пользователь мог использовать оба, когда дело касается - например, - установки цвета. Так что это не совсем решение. Возможно, это сработает с герцем, но это всего лишь одна единица. –

ответ

17

Я знаю, что вы упомянули, вы не используете 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 
+0

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

8

Библиотека «Единицы» Boost отлично подходит для такого типа вещей.

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

+1

Я не уверен, что это хорошая идея. Я имею в виду - Boost отлично работает и, конечно же, предлагает много, но я думаю, что это похоже на использование дорожного ролика для взлома гайки в этом случае. Предпочитал бы получить более простое решение, которое не требует дополнительных библиотек. Хотя это действительное решение, так что я пока буду его продвигать :) –

+2

@ PawełStawarz Согласен. Если вы еще не используете Boost, это может показаться дорогостоящим/трудным для начала. Если вы * уже используете Boost, то это очень просто. Как только вы преодолеете этот «горб», есть целый мир, полный ожидающих вас! – aldo

+0

Существует также [PhysUnits-CT-Cpp11] (https://github.com/martinmoene/PhysUnits-CT-Cpp11), небольшая библиотека заголовков C++ 11, C++ 14 для анализа размеров времени компиляции и манипуляция единицей/количеством и преобразование. Упрощен, чем Boost.Units, зависит только от стандартной библиотеки C++, SI-only, целых степеней размеров –

4

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

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