-
-
Save simendsjo/1424475 to your computer and use it in GitHub Desktop.
siunit
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import std.traits, std.stdio, std.conv; | |
void main() { | |
auto f = feet(10); | |
auto m = meter(10.0); // kan fint bruke desimaltall | |
// en type til en annen - konvertering | |
auto feetMeter = f + m; | |
assert( feetMeter.siunit == "feet" ); | |
assert( feetMeter >= feet(42.808) && | |
feetMeter <= feet(42.809) ); | |
auto meterFeet = m + f; | |
assert( meterFeet.siunit == "meter" ); | |
assert( meterFeet >= meter(13.0475) && | |
meterFeet <= meter(13.0485) ); | |
// kan også bruke skalarer - og fint mikse floats inn i det | |
assert( m * 2.2 == meter(22.0) ); | |
// eller konvertere direkte | |
auto f2 = meterFeet.to!"feet"(); | |
assert( f2.siunit == "feet" ); | |
assert( f2 >= feet(42.808) && | |
f2 <= feet(42.809) ); | |
// men kan ikke konvertere uten konverteringsfunksjoner | |
static assert(!__traits(compiles, m.to!"finnes ikke"()) ); | |
// men kan ikke manipulere urelaterte typer | |
static assert(!__traits(compiles, feet(10) + celsius(10)) ); | |
// kan caste til kompatible typer | |
double d = cast(double)m; | |
assert(d == 10.0); | |
// eller sammenlikne | |
assert( m == 10 ); | |
assert( m > 9 ); | |
assert( m < 11 ); | |
} | |
auto meter(V)(V value) pure nothrow @safe { | |
return Value!("meter", V)(value); | |
} | |
auto feet(V)(V value) pure nothrow @safe { | |
return Value!("feet", V)(value); | |
} | |
auto celsius(V)(V value) pure nothrow @safe { | |
return Value!("celsius", V)(value); | |
} | |
auto fahrenheit(V)(V value) pure nothrow @safe { | |
return Value!("fahrenheit", V)(value); | |
} | |
auto SIConvert(string S, string D, T)(T value) pure nothrow @safe | |
if(S == "meter" && D == "feet") { | |
pragma(msg, "conv:meter>feet"); | |
return value / 0.3048; | |
} | |
auto SIConvert(string S, string D, T)(T value) pure nothrow @safe | |
if(S == "feet" && D == "meter") { | |
pragma(msg, "conv:feet>meter"); | |
return value * 0.3048; | |
} | |
auto SIConvert(string S, string D, T)(T value) pure nothrow @safe | |
if(S == "celsius" && D == "fahrenheit") { | |
return (value - 32) * (5/9); | |
} | |
auto SIConvert(string S, string D, T)(T value) pure nothrow @safe | |
if(S == "fahrenheit" && D == "celsius") { | |
return value * (9/5) + 32; | |
} | |
template isSIValue(T) { | |
// HACK: Sjekker kun om det har en unit verdi... | |
static if( is(typeof(T.siunit) : string) ) | |
enum isSIValue = true; | |
else | |
enum isSIValue = false; | |
} | |
auto SIConvert(S, D, T)(T value) pure nothrow @safe | |
if(isSIValue!S && isSIValue!D) { | |
static if( S.siunit == D.siunit ) | |
return value; | |
else { | |
static assert(__traits(compiles, SIConvert!(S.siunit, D.siunit, T)(value)), | |
"Cannot convert value of type "~T.stringof~ | |
" from "~S.siunit~" to "~D.siunit); | |
return SIConvert!(S.siunit, D.siunit, T)(value); | |
} | |
} | |
struct Value(string SIUnit, V) if(isNumeric!V) { | |
enum siunit = SIUnit; | |
immutable V value; | |
this(V)(V value) pure nothrow @safe { | |
this.value = value; | |
} | |
auto opBinary(string op, RHS)(RHS r) const nothrow pure @safe | |
if(isSIValue!RHS) { | |
auto converted = SIConvert!(RHS, typeof(this))(r.value); | |
mixin("auto newval = value "~op~" converted;"); | |
return Value!(siunit, typeof(newval))(newval); | |
} | |
auto opBinary(string op, RHS)(const RHS r) const nothrow pure @safe | |
if(isNumeric!RHS) { | |
mixin("auto val = value "~op~" r;"); | |
return Value!(siunit, typeof(val))(val); | |
} | |
auto to(string unit)() { | |
auto val = SIConvert!(siunit, unit, typeof(value))(value); | |
return Value!(unit, typeof(val))(val); | |
} | |
T opCast(T)() const pure nothrow @safe | |
if(is(typeof(value) : T)) { | |
return cast(T)value; | |
} | |
bool opEquals(T)(ref const T o) const pure nothrow @safe { | |
return o.siunit == siunit && o.value == value; | |
} | |
int opCmp(T)(ref const T v) const pure nothrow @safe | |
if(isSIValue!T) { | |
static assert(T.siunit == siunit, "Cannot compare "~siunit~" to "~T.siunit~ | |
". Try converting the value first"); | |
return value < v ? -1 : (value > v ? 1 : 0); | |
} | |
int opCmp(T)(T o) const pure nothrow @safe | |
if(isNumeric!T) { | |
return value < o ? -1 : (value > o ? 1 : 0); | |
} | |
string toString() const @trusted { | |
return .to!string(value)~" "~siunit; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment