2014-02-19 7 views
12

Ich arbeite an einer Game-Engine, und derzeit stehe ich fest, das IO-System zu entwerfen. Ich habe es so gemacht, die Engine selbst behandelt keine Dateiformate, sondern lässt den Benutzer alles implementieren, was er will, indem er eine *.dll Datei mit nützlichen Funktionen erstellt. Während das selbst kein großes Problem war, sind meine Hauptanliegen die Implikationen, die wahrscheinlich während der Verwendung der Engine sichtbar werden.Maßeinheiten in C++

Ich entwarf eine einfache resource Schnittstelle als Basisklasse für alle Dinge, die der Benutzer ausdenken kann, und ich versuche, es zu erweitern, indem einfache untergeordnete Klassen für die gemeinsamen Datentypen gewidmet, so dass der Benutzer nicht muss die Grundlagen selbst implementieren (derzeit denke ich an audio, image, data und). Beginnend mit der audio Klasse, habe ich auf ein besonderes Problem sublobiert, während zu versuchen zu entscheiden, in welchem ​​Typ ich Informationen über Abtastrate speichern sollte. Die übliche Einheit sind Hertz, also entschied ich mich, es zu unsigned int zu machen.

Allerdings gibt es ein kleines Problem hier - was ist, wenn der Benutzer versucht, es in Kilohertz setzen? Nehmen wir an, dass ein abstraktes Dateiformat es in beiden Einheiten für einen Moment speichern kann. Ich habe eine einfache Wrapper-Klasse machte den Einheitentyp zu nennen:

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

und entschied sich der Benutzer auch kHz verwenden zu lassen:

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

Während die Funktion innerhalb der audio Klasse, die es dem Benutzer ermöglicht Stellen Sie die Abtastrate als track& samplingRate(units::hertz rate); deklariert. Der Benutzer hat es zu nennen explizit zu sagen, was Größenordnung er verwendet:

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

Meine Frage ist:

Gibt es einen besseren Weg, um die Benutzer zu zwingen, eine Messeinheit in einem einfach zu bedienen und eleganter Weg? Vielleicht ein Designmuster oder eine clevere Verwendung von typedefs?

Bitte beachten Sie auch, dass ich bei der Erstellung der Engine möglicherweise mehr Einheiten benötige, die nicht mit Hertz kompatibel sind. Von Anfang an möchte ich, dass der Benutzer eine Pixelfarbe festlegen kann, indem er units::rgb(123,42,120) und units::hsl(10,30,240) ausführt.

Ich habe versucht, eine tragfähige Antwort zu suchen und fand nur this question, aber das OP wollte nur Größenordnungen, ohne sicherzustellen, dass die Einheiten nicht mit anderen kompatibel sind.

Bitte beachten Sie auch, ich verwende die alte C++ Version, nicht C++11. Während das Posten einer in jeder Version gültigen Lösung großartig ist, wäre es schön, wenn ich es auch benutzen könnte :)

+1

Können Sie nicht einfach Ihre API richtig dokumentieren? –

+1

Ja, um Verwirrung zu vermeiden, sollten Sie Ihre API auf einen Standard, Hertz oder Kilohertz, beschränken, nicht beides. – leetNightshade

+0

Dokumentieren meiner API wird nichts ändern, da ich möchte, dass der Benutzer beide verwenden kann, wenn es zum Beispiel darum geht, eine Farbe einzustellen. Das ist also nicht gerade eine Lösung. Vielleicht würde es mit Hertz funktionieren, aber das ist nur eine Einheit. –

Antwort

17

Ich weiß, Sie erwähnten Sie nicht auf diese Frage suchen C++ 11, aber andere verwenden kann, also hier ist die C++ 11-Lösung benutzerdefiniert Literale mit:

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

Ausgang:

44100Hz 
44100Hz 
+0

Nun ... da ich C++ 11 nicht verwenden kann, werde ich bei meiner Lösung bleiben. Aber ich würde das vollkommen nutzen, wenn ich eine neuere Version meiner IDE hätte. Also - ich markiere das als die Antwort, für zukünftige Generationen zu wissen :) –

8

Die Boost "Units" -Bibliothek ist großartig für diese Art von Sache.

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

+1

Ich bin mir nicht sicher, ob das eine gute Idee ist. Ich meine - Boost ist großartig und bietet sicherlich eine Menge, aber ich denke, es ist, als ob man in diesem Fall eine Straßenwalze benutzt, um eine Nuss zu knacken. Lieber eine einfachere Lösung, die keine zusätzlichen Bibliotheken benötigt. Obwohl dies eine gültige Lösung ist, werde ich es für jetzt upvote :) –

+2

@ PawełStawarz Einverstanden. Wenn Sie Boost nicht bereits verwenden, kann es teuer/schwierig erscheinen, anzufangen. Wenn du Boost bereits * verwendest, ist es wirklich einfach. Sobald du diesen "Buckel" überwunden hast, wartet eine ganze Welt voller toller Sachen auf dich! – aldo

+0

Es gibt auch [PhysUnits-CT-Cpp11] (https://github.com/martinmoene/PhysUnits-CT-Cpp11), eine kleine C++ 11, C++ 14-Header-only-Bibliothek für die Kompilierzeit-Dimensionsanalyse und Einheit/Menge Manipulation und Konvertierung. Simpler als Boost.Units, nur abhängig von Standard-C++ - Bibliothek, SI-only, integrale Kräfte der Dimensionen –

4

Sie können das factory design pattern verwenden, um zu erreichen, wonach Sie suchen. Sie können eine Häufigkeitsklasse mit einem privaten Konstruktor und mehreren statischen Methoden erstellen, die das Objekt abhängig von den Einheiten erstellen, die der Benutzer verwenden möchte. Indem der Konstruktor privat gehalten wird, muss der Benutzer seine Einheiten explizit deklarieren, was die Wahrscheinlichkeit eines Benutzerfehlers verringert.

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