@@ -1,237 +1,237 @@ -/** - * Authors: k.inaba - * License: NYSL 0.9982 http://www.kmonos.net/nysl/ - * - * Common tricks and utilities for programming in D. - */ -module polemy.tricks; -import std.array : appender; -import std.format : formattedWrite; -import core.exception : onAssertErrorMsg, AssertError; - -/// Simple Wrapper for std.format.doFormat - -string sprintf(string fmt, T...)(T params) -{ - auto writer = appender!string(); - formattedWrite(writer, fmt, params); - return writer.data; -} - -unittest -{ - assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" ); - assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" ); -} - -/// Unittest helper that asserts an expression must throw something - -void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") -{ - try { - t(); - } catch(ExceptionType) { - return; - } catch(Throwable e) { - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } - onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); -} - -/// Unittest helper that asserts an expression must not throw anything - -void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") -{ - try { - t(); - } catch(Throwable e) { - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } -} - -unittest -{ - auto error = {throw new Error("hello");}; - auto nothing = (){}; - auto assertError = {assert(0);}; - - assert_nothrow ( assert_nothrow(nothing()) ); - assert_throw!AssertError( assert_nothrow(error()) ); - assert_throw!AssertError( assert_nothrow(assertError()) ); - - assert_nothrow ( assert_throw!Error(error()) ); - assert_throw!AssertError( assert_throw!Error(nothing()) ); - assert_nothrow ( assert_throw!Error(assertError()) ); - assert_throw!AssertError( assert_throw!AssertError(error()) ); -} - -/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= - -template assertOp(string op) -{ - void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="") - { - try { - if( mixin("a"~op~"b") ) return; - } catch(Throwable e) { - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); - } - onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); - } -} - -alias assertOp!(`==`) assert_eq; -alias assertOp!(`!=`) assert_ne; -alias assertOp!(`<`) assert_lt; -alias assertOp!(`<=`) assert_le; -alias assertOp!(`>`) assert_gt; -alias assertOp!(`>=`) assert_ge; - -unittest -{ - assert_nothrow( assert_eq("foo", "foo") ); - assert_nothrow( assert_ne("foo", "bar") ); - assert_nothrow( assert_lt("bar", "foo") ); - assert_nothrow( assert_le("bar", "foo") ); - assert_nothrow( assert_le("bar", "bar") ); - assert_nothrow( assert_gt("foo", "bar") ); - assert_nothrow( assert_ge("foo", "bar") ); - assert_nothrow( assert_ge("bar", "bar") ); - - assert_throw!AssertError( assert_eq("foo", "bar") ); - assert_throw!AssertError( assert_ne("foo", "foo") ); - assert_throw!AssertError( assert_lt("foo", "foo") ); - assert_throw!AssertError( assert_lt("foo", "bar") ); - assert_throw!AssertError( assert_le("foo", "bar") ); - assert_throw!AssertError( assert_gt("bar", "bar") ); - assert_throw!AssertError( assert_gt("bar", "foo") ); - assert_throw!AssertError( assert_ge("bar", "foo") ); - - class Temp { bool opEquals(int x){return x/x==x;} } - assert_throw!AssertError( assert_eq(new Temp, 0) ); - assert_nothrow ( assert_eq(new Temp, 1) ); - assert_throw!AssertError( assert_eq(new Temp, 2) ); -} - -/* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */ - -/// Mixing-in the bean constructor for a class - -template SimpleConstructor() -{ - static if( is(typeof(super) == Object) || super.tupleof.length==0 ) - this( typeof(this.tupleof) params ) - { - static if(this.tupleof.length>0) - this.tupleof = params; - } - else - this( typeof(super.tupleof) ps, typeof(this.tupleof) params ) - { - // including (only) the direct super class members - // may not always be a desirable choice, but should work for many cases - super(ps); - static if(this.tupleof.length>0) - this.tupleof = params; - } -} - -unittest -{ - class Temp - { - int x; - string y; - mixin SimpleConstructor; - } - assert_eq( (new Temp(1,"foo")).x, 1 ); - assert_eq( (new Temp(1,"foo")).y, "foo" ); - assert( !__traits(compiles, new Temp) ); - assert( !__traits(compiles, new Temp(1)) ); - assert( !__traits(compiles, new Temp("foo",1)) ); - - class Tomp : Temp - { - real z; - mixin SimpleConstructor; - } - assert_eq( (new Tomp(1,"foo",2.5)).x, 1 ); - assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" ); - assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 ); - assert( !__traits(compiles, new Tomp(3.14)) ); - - // shiyo- desu. Don't use in this way. - // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), - // but it fails because Tomp takes (int,string,real). - assert( !__traits(compiles, { - class Tamp : Tomp - { - mixin SimpleConstructor; - } - }) ); -} - -/// Mixing-in the MOST-DERIVED-member-wise comparator for a class - -template SimpleCompare() -{ - override bool opEquals(Object rhs_) const - { - if( auto rhs = cast(typeof(this))rhs_ ) - { - foreach(i,_; this.tupleof) - if( this.tupleof[i] != rhs.tupleof[i] ) - return false; - return true; - } - assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); - } - - override hash_t toHash() const - { - hash_t h = 0; - foreach(mem; this.tupleof) - h += typeid(mem).getHash(&mem); - return h; - } - - override int opCmp(Object rhs_) const - { - if( auto rhs = cast(typeof(this))rhs_ ) - { - foreach(i,_; this.tupleof) - if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i])) - return c; - return 0; - } - assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); - } -} - -unittest -{ - class Temp - { - int x; - string y; - mixin SimpleConstructor; - mixin SimpleCompare; - } - assert_eq( new Temp(1,"foo"), new Temp(1,"foo") ); - assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash ); - assert_ne( new Temp(1,"foo"), new Temp(2,"foo") ); - assert_ne( new Temp(1,"foo"), new Temp(1,"bar") ); - assert_gt( new Temp(1,"foo"), new Temp(1,"bar") ); - assert_lt( new Temp(1,"foo"), new Temp(2,"bar") ); - assert_ge( new Temp(1,"foo"), new Temp(1,"foo") ); - - class TempDummy - { - int x; - string y; - mixin SimpleConstructor; - mixin SimpleCompare; - } - assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); - assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); -} +/** + * Authors: k.inaba + * License: NYSL 0.9982 http://www.kmonos.net/nysl/ + * + * Common tricks and utilities for programming in D. + */ +module polemy.tricks; +import std.array : appender; +import std.format : formattedWrite; +import core.exception : onAssertErrorMsg, AssertError; + +/// Simple Wrapper for std.format.doFormat + +string sprintf(string fmt, T...)(T params) +{ + auto writer = appender!string(); + formattedWrite(writer, fmt, params); + return writer.data; +} + +unittest +{ + assert( sprintf!"%s == %d"("1+2", 3) == "1+2 == 3" ); + assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" ); +} + +/// Unittest helper that asserts an expression must throw something + +void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +{ + try { + t(); + } catch(ExceptionType) { + return; + } catch(Throwable e) { + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + } + onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption"); +} + +/// Unittest helper that asserts an expression must not throw anything + +void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="") +{ + try { + t(); + } catch(Throwable e) { + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + } +} + +unittest +{ + auto error = {throw new Error("hello");}; + auto nothing = (){}; + auto assertError = {assert(0);}; + + assert_nothrow ( assert_nothrow(nothing()) ); + assert_throw!AssertError( assert_nothrow(error()) ); + assert_throw!AssertError( assert_nothrow(assertError()) ); + + assert_nothrow ( assert_throw!Error(error()) ); + assert_throw!AssertError( assert_throw!Error(nothing()) ); + assert_nothrow ( assert_throw!Error(assertError()) ); + assert_throw!AssertError( assert_throw!AssertError(error()) ); +} + +/// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >= + +template assertOp(string op) +{ + void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="") + { + try { + if( mixin("a"~op~"b") ) return; + } catch(Throwable e) { + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e)); + } + onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b)); + } +} + +alias assertOp!(`==`) assert_eq; +alias assertOp!(`!=`) assert_ne; +alias assertOp!(`<`) assert_lt; +alias assertOp!(`<=`) assert_le; +alias assertOp!(`>`) assert_gt; +alias assertOp!(`>=`) assert_ge; + +unittest +{ + assert_nothrow( assert_eq(1, 1) ); + assert_nothrow( assert_ne(1, 0) ); + assert_nothrow( assert_lt(0, 1) ); + assert_nothrow( assert_le(0, 1) ); + assert_nothrow( assert_le(0, 0) ); + assert_nothrow( assert_gt(1, 0) ); + assert_nothrow( assert_ge(1, 0) ); + assert_nothrow( assert_ge(0, 0) ); + + assert_throw!AssertError( assert_eq(1, 0) ); + assert_throw!AssertError( assert_ne(1, 1) ); + assert_throw!AssertError( assert_lt(1, 1) ); + assert_throw!AssertError( assert_lt(1, 0) ); + assert_throw!AssertError( assert_le(1, 0) ); + assert_throw!AssertError( assert_gt(0, 0) ); + assert_throw!AssertError( assert_gt(0, 1) ); + assert_throw!AssertError( assert_ge(0, 1) ); + + class Temp { bool opEquals(int x){return x/x==x;} } + assert_throw!AssertError( assert_eq(new Temp, 0) ); + assert_nothrow ( assert_eq(new Temp, 1) ); + assert_throw!AssertError( assert_eq(new Temp, 2) ); +} + +/* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */ + +/// Mixing-in the bean constructor for a class + +template SimpleConstructor() +{ + static if( is(typeof(super) == Object) || super.tupleof.length==0 ) + this( typeof(this.tupleof) params ) + { + static if(this.tupleof.length>0) + this.tupleof = params; + } + else + this( typeof(super.tupleof) ps, typeof(this.tupleof) params ) + { + // including (only) the direct super class members + // may not always be a desirable choice, but should work for many cases + super(ps); + static if(this.tupleof.length>0) + this.tupleof = params; + } +} + +unittest +{ + class Temp + { + int x; + string y; + mixin SimpleConstructor; + } + assert_eq( (new Temp(1,"foo")).x, 1 ); + assert_eq( (new Temp(1,"foo")).y, "foo" ); + assert( !__traits(compiles, new Temp) ); + assert( !__traits(compiles, new Temp(1)) ); + assert( !__traits(compiles, new Temp("foo",1)) ); + + class Tomp : Temp + { + real z; + mixin SimpleConstructor; + } + assert_eq( (new Tomp(1,"foo",2.5)).x, 1 ); + assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" ); + assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 ); + assert( !__traits(compiles, new Tomp(3.14)) ); + + // shiyo- desu. Don't use in this way. + // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), + // but it fails because Tomp takes (int,string,real). + assert( !__traits(compiles, { + class Tamp : Tomp + { + mixin SimpleConstructor; + } + }) ); +} + +/// Mixing-in the MOST-DERIVED-member-wise comparator for a class + +template SimpleCompare() +{ + override bool opEquals(Object rhs_) const + { + if( auto rhs = cast(typeof(this))rhs_ ) + { + foreach(i,_; this.tupleof) + if( this.tupleof[i] != rhs.tupleof[i] ) + return false; + return true; + } + assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); + } + + override hash_t toHash() const + { + hash_t h = 0; + foreach(mem; this.tupleof) + h += typeid(mem).getHash(&mem); + return h; + } + + override int opCmp(Object rhs_) const + { + if( auto rhs = cast(typeof(this))rhs_ ) + { + foreach(i,_; this.tupleof) + if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i])) + return c; + return 0; + } + assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); + } +} + +unittest +{ + class Temp + { + int x; + string y; + mixin SimpleConstructor; + mixin SimpleCompare; + } + assert_eq( new Temp(1,"foo"), new Temp(1,"foo") ); + assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash ); + assert_ne( new Temp(1,"foo"), new Temp(2,"foo") ); + assert_ne( new Temp(1,"foo"), new Temp(1,"bar") ); + assert_gt( new Temp(1,"foo"), new Temp(1,"bar") ); + assert_lt( new Temp(1,"foo"), new Temp(2,"bar") ); + assert_ge( new Temp(1,"foo"), new Temp(1,"foo") ); + + class TempDummy + { + int x; + string y; + mixin SimpleConstructor; + mixin SimpleCompare; + } + assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") ); + assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") ); +}