www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Compile time metaprogramming

reply simendsjo <simendsjo gmail.com> writes:
I had a little talk with one of my teachers regarding the culprits of 
wrong unit assumptions in code 
(http://mars.jpl.nasa.gov/msp98/news/mco990930.html).

We had a little different views on how much pain it would be for 
developers to code using an SI library, so I hacked together a small 
proof-of-concept in D to show how D's metaprogramming could make things 
a lot less painful than, say, Java.

I thought I could share it here too even though it's just a small hack 
with Norwegian comments.


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
     // kan gjerne bruke /, * e.l. også - metaprogrammering er en god 
ting :)
     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) );

     // 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 direkte
     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") {
     return value / 0.3048;
}

auto SIConvert(string S, string D, T)(T value) pure nothrow  safe
if(S == "feet" && D == "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; // finnes kun ved kompilering
     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)(const T o) const pure nothrow  safe
     if(isSIValue!T) {
         return o.siunit == siunit && o.value == value;
     }

     bool opEquals(T)(const T o) const pure nothrow  safe
     if(is(T : typeof(value))) {
         return value == o;
     }

     int opCmp(T)(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)(const 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;
     }
}
Dec 02 2011
next sibling parent reply "Jonathan M Davis" <jmdavisProg gmx.com> writes:
On Friday, December 02, 2011 22:00:44 simendsjo wrote:
 I had a little talk with one of my teachers regarding the culprits of
 wrong unit assumptions in code
 (http://mars.jpl.nasa.gov/msp98/news/mco990930.html).
 
 We had a little different views on how much pain it would be for
 developers to code using an SI library, so I hacked together a small
 proof-of-concept in D to show how D's metaprogramming could make things
 a lot less painful than, say, Java.
 
 I thought I could share it here too even though it's just a small hack
 with Norwegian comments.

A unit library is definitely a great place for taking advantage of templates - that's what core.time and std.datetime do with time units (e.g. dur!"seconds"(5)). There's also been at least a couple of cases where people have worked on unit libraries and discussed them in the main newsgroup, but so far, nothing has gotten to the point where it's been reviewed for introduction to Phobos, and I don't know if any of those projects is still alive. It would definitely be an asset though. - Jonathan M Davis
Dec 02 2011
parent reply simendsjo <simendsjo gmail.com> writes:
On 02.12.2011 23:28, Jonathan M Davis wrote:
 There's also been at least a couple of cases where people
 have worked on unit libraries and discussed them in the main newsgroup, but so
 far, nothing has gotten to the point where it's been reviewed for introduction
 to Phobos, and I don't know if any of those projects is still alive. It would
 definitely be an asset though.

I seem to remember someone wanting to work on a library for GSoC and that he would work on it even if it wasn't accepted. I might remember wrong, and the person might have left the project.
Dec 02 2011
parent reply David Nadlinger <see klickverbot.at> writes:
On 12/3/11 1:43 AM, simendsjo wrote:
 On 02.12.2011 23:28, Jonathan M Davis wrote:
 There's also been at least a couple of cases where people
 have worked on unit libraries and discussed them in the main
 newsgroup, but so
 far, nothing has gotten to the point where it's been reviewed for
 introduction
 to Phobos, and I don't know if any of those projects is still alive.
 It would
 definitely be an asset though.

I seem to remember someone wanting to work on a library for GSoC and that he would work on it even if it wasn't accepted. I might remember wrong, and the person might have left the project.

That was Cristi Cobzarenco, but he then proposed a different project, linear algebra stuff [1], which he ended up working on because I already had a more or less working implementation of an units library lying around: [2]. I posted my project to the NG, and there seemed to actually be two or three people interested in it, but I didn't submit it to formal review yet, because it sometimes breaks in interesting ways due to compiler bugs (issue 3467 [3] and the likes), and I had enough work to do for my own GSoC project anyway. David [1] http://www.google-melange.com/gsoc/project/google/gsoc2011/cristicbz/36001 [2] http://klickverbot.at/code/units/ [3] http://d.puremagic.com/issues/show_bug.cgi?id=3467
Dec 03 2011
next sibling parent Dejan Lekic <dejan.lekic gmail.com> writes:
David, to be frank, your code is already useful! Something is better than 
*nothing*! I hope you or someone else will continue with these two modules, 
and include them in Phobos.
Dec 03 2011
prev sibling parent reply simendsjo <simendsjo gmail.com> writes:
On 03.12.2011 10:26, David Nadlinger wrote:
 I posted my project to the NG, and there seemed to actually be two or
 three people interested in it, but I didn't submit it to formal review
 yet, because it sometimes breaks in interesting ways due to compiler
 bugs (issue 3467 [3] and the likes), and I had enough work to do for my
 own GSoC project anyway.

Seems one of your bugs recently got a pull request: https://github.com/D-Programming-Language/dmd/pull/449 Gotta love the move to github!
Dec 03 2011
parent David Nadlinger <see klickverbot.at> writes:
On 12/3/11 1:49 PM, simendsjo wrote:
 Seems one of your bugs recently got a pull request:
 https://github.com/D-Programming-Language/dmd/pull/449

I'm aware of that, though Walter apparently still thinks it's okay for foo!3u and foo!3 to be different things given »template foo(uint u)«… David
Dec 03 2011
prev sibling parent =?utf-8?Q?Simen_Kj=C3=A6r=C3=A5s?= <simen.kjaras gmail.com> writes:
On Sat, 03 Dec 2011 10:26:09 +0100, David Nadlinger <see klickverbot.at>  
wrote:

 On 12/3/11 1:43 AM, simendsjo wrote:
 On 02.12.2011 23:28, Jonathan M Davis wrote:
 There's also been at least a couple of cases where people
 have worked on unit libraries and discussed them in the main
 newsgroup, but so
 far, nothing has gotten to the point where it's been reviewed for
 introduction
 to Phobos, and I don't know if any of those projects is still alive.
 It would
 definitely be an asset though.

I seem to remember someone wanting to work on a library for GSoC and that he would work on it even if it wasn't accepted. I might remember wrong, and the person might have left the project.

That was Cristi Cobzarenco, but he then proposed a different project, linear algebra stuff [1], which he ended up working on because I already had a more or less working implementation of an units library lying around: [2]. I posted my project to the NG, and there seemed to actually be two or three people interested in it, but I didn't submit it to formal review yet, because it sometimes breaks in interesting ways due to compiler bugs (issue 3467 [3] and the likes), and I had enough work to do for my own GSoC project anyway.

I, for one, wholeheartedly support the inclusion of this in Phobos. Oh, and Walter's reply to #3467 is scary. I hope he comes around to seeing he's not making sense.
Dec 04 2011