8de5b49cdf 2010-11-09 kinaba: /** 8de5b49cdf 2010-11-09 kinaba: * Authors: k.inaba 8de5b49cdf 2010-11-09 kinaba: * License: NYSL 0.9982 http://www.kmonos.net/nysl/ 8de5b49cdf 2010-11-09 kinaba: * 8de5b49cdf 2010-11-09 kinaba: * Common tricks and utilities for programming in D. 8de5b49cdf 2010-11-09 kinaba: */ 8de5b49cdf 2010-11-09 kinaba: module tricks.tricks; 8de5b49cdf 2010-11-09 kinaba: import tricks.test; 1c01f44f52 2010-11-13 kinaba: import core.exception; 515502e8d1 2010-11-20 kinaba: import std.array : appender; 515502e8d1 2010-11-20 kinaba: import std.format : formattedWrite; 1c01f44f52 2010-11-13 kinaba: import std.traits; 1c01f44f52 2010-11-13 kinaba: import std.typetuple; 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /// Simple Wrapper for std.format.doFormat 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: string sprintf(string fmt, T...)(T params) 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: auto writer = appender!string(); 8de5b49cdf 2010-11-09 kinaba: formattedWrite(writer, fmt, params); 8de5b49cdf 2010-11-09 kinaba: return writer.data; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: unittest 8de5b49cdf 2010-11-09 kinaba: { 7de80acfb8 2010-11-09 kinaba: assert_eq( sprintf!"%s == %04d"("1+2", 3), "1+2 == 0003" ); 7de80acfb8 2010-11-09 kinaba: assert_eq( sprintf!"%2$s == %1$s"("1+2", 5, 8), "5 == 1+2" ); 7de80acfb8 2010-11-09 kinaba: assert_throw!Error( sprintf!"%s%s"(1) ); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /// Create an exception with automatically completed filename and lineno information 8de5b49cdf 2010-11-09 kinaba: 38fcc662be 2010-11-10 kinaba: ExceptionType genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params) 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: static if( T.length > 0 && is(T[$-1] : Throwable) ) 8de5b49cdf 2010-11-09 kinaba: return new ExceptionType(params[0..$-1], fn, ln, params[$-1]); 8de5b49cdf 2010-11-09 kinaba: else 8de5b49cdf 2010-11-09 kinaba: return new ExceptionType(params, fn, ln); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: unittest 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: assert_ne( genex!Exception("msg").file, "" ); 8de5b49cdf 2010-11-09 kinaba: assert_ne( genex!Exception("msg").line, 0 ); 8de5b49cdf 2010-11-09 kinaba: assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init ); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /// Mixing-in the bean constructor for a class 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /*mixin*/ 8de5b49cdf 2010-11-09 kinaba: template SimpleConstructor() 8de5b49cdf 2010-11-09 kinaba: { 38fcc662be 2010-11-10 kinaba: /// member-by-member constructor 8de5b49cdf 2010-11-09 kinaba: static if( is(typeof(super) == Object) || super.tupleof.length==0 ) 8de5b49cdf 2010-11-09 kinaba: this( typeof(this.tupleof) params ) 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: static if(this.tupleof.length>0) 8de5b49cdf 2010-11-09 kinaba: this.tupleof = params; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: else 8de5b49cdf 2010-11-09 kinaba: this( typeof(super.tupleof) ps, typeof(this.tupleof) params ) 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: // including (only) the direct super class members 8de5b49cdf 2010-11-09 kinaba: // may not always be a desirable choice, but should work for many cases 8de5b49cdf 2010-11-09 kinaba: super(ps); 8de5b49cdf 2010-11-09 kinaba: static if(this.tupleof.length>0) 8de5b49cdf 2010-11-09 kinaba: this.tupleof = params; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: unittest 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: class Temp 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: int x; 8de5b49cdf 2010-11-09 kinaba: string y; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Temp(1,"foo")).x, 1 ); 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Temp(1,"foo")).y, "foo" ); 8de5b49cdf 2010-11-09 kinaba: assert( !__traits(compiles, new Temp) ); 8de5b49cdf 2010-11-09 kinaba: assert( !__traits(compiles, new Temp(1)) ); 8de5b49cdf 2010-11-09 kinaba: assert( !__traits(compiles, new Temp("foo",1)) ); 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: class Tomp : Temp 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: real z; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).x, 1 ); 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" ); 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 ); 8de5b49cdf 2010-11-09 kinaba: assert( !__traits(compiles, new Tomp(3.14)) ); 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: // shiyo- desu. Don't use in this way. 8de5b49cdf 2010-11-09 kinaba: // Tamp tries to call new Tomp(real) (because it only sees Tomp's members), 8de5b49cdf 2010-11-09 kinaba: // but it fails because Tomp takes (int,string,real). 8de5b49cdf 2010-11-09 kinaba: assert( !__traits(compiles, { 38fcc662be 2010-11-10 kinaba: class Tamp : Tomp { mixin SimpleConstructor; } 8de5b49cdf 2010-11-09 kinaba: }) ); 38fcc662be 2010-11-10 kinaba: } 38fcc662be 2010-11-10 kinaba: b993a8ad16 2010-11-24 kinaba: hash_t structuralHash(T)(T x) b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: alias SC_Unqual!(T) UCT; b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: static if(is(UCT == class)) b993a8ad16 2010-11-24 kinaba: return (cast(UCT)x).toHash(); b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static if(SC_HasGoodHash!(UCT)) b993a8ad16 2010-11-24 kinaba: { return typeid(UCT).getHash(&x); } b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static if(is(UCT T == T[])) b993a8ad16 2010-11-24 kinaba: { hash_t h; foreach(e; x) h+=structuralHash(e); return h; } b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static if(is(UCT == struct)) b993a8ad16 2010-11-24 kinaba: static if(__traits(compiles, std.bigint.BigInt)) b993a8ad16 2010-11-24 kinaba: static if(is(UCT == std.bigint.BigInt)) b993a8ad16 2010-11-24 kinaba: return cast(hash_t) x.toInt(); b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static assert(false, "should not use struct.toHash"); b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static assert(false, "should not use struct.toHash"); b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: static assert(false, "nonhashable datatype "~UCT.stringof); b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: alias std.traits.Unqual SC_Unqual; b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: template SC_HasGoodHash(T) b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: enum SC_HasGoodHash = b993a8ad16 2010-11-24 kinaba: is(T : bool) || isNumeric!(T) || isSomeString!(T) || isSomeChar!(T) || isPointer!(T); b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: b97bd4f713 2010-11-23 kinaba: /// Mixing-in the MOST-DERIVED-member-wise comparator for a class b993a8ad16 2010-11-24 kinaba: /// BE SURE THAT THIS IS CONSISTENT WITH opCmp and opEquals b97bd4f713 2010-11-23 kinaba: b97bd4f713 2010-11-23 kinaba: template SimpleToHash() b97bd4f713 2010-11-23 kinaba: { 38fcc662be 2010-11-10 kinaba: override hash_t toHash() const /// member-by-member hash 38fcc662be 2010-11-10 kinaba: { 38fcc662be 2010-11-10 kinaba: hash_t h = 0; 38fcc662be 2010-11-10 kinaba: foreach(mem; this.tupleof) b993a8ad16 2010-11-24 kinaba: h += structuralHash(mem); 38fcc662be 2010-11-10 kinaba: return h; b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: /// Mixing-in the MOST-DERIVED-member-wise comparator for a class b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: template SimpleCompareWithoutToHash() b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: override bool opEquals(Object rhs) const /// member-by-member equality b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: return opCmp(rhs) == 0; b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: b993a8ad16 2010-11-24 kinaba: override int opCmp(Object rhs_) const /// member-by-member compare b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: if( rhs_ is null ) b993a8ad16 2010-11-24 kinaba: return -1; b993a8ad16 2010-11-24 kinaba: if( auto rhs = cast(typeof(this))rhs_ ) b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: foreach(i,_; this.tupleof) b993a8ad16 2010-11-24 kinaba: { b993a8ad16 2010-11-24 kinaba: static if(is(typeof(_) == struct)) b993a8ad16 2010-11-24 kinaba: auto c = (cast(SC_Unqual!(typeof(_)))this.tupleof[i]).opCmp(rhs.tupleof[i]); b993a8ad16 2010-11-24 kinaba: else b993a8ad16 2010-11-24 kinaba: auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]); b993a8ad16 2010-11-24 kinaba: if(c) b993a8ad16 2010-11-24 kinaba: return c; b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: return 0; b993a8ad16 2010-11-24 kinaba: } b993a8ad16 2010-11-24 kinaba: return typeid(this).opCmp(typeid(rhs_)); 38fcc662be 2010-11-10 kinaba: } 078444a806 2010-11-13 kinaba: } 078444a806 2010-11-13 kinaba: b97bd4f713 2010-11-23 kinaba: /// Mixing-in the MOST-DERIVED-member-wise comparator for a class b97bd4f713 2010-11-23 kinaba: b97bd4f713 2010-11-23 kinaba: /*mixin*/ b97bd4f713 2010-11-23 kinaba: template SimpleCompare() b97bd4f713 2010-11-23 kinaba: { b97bd4f713 2010-11-23 kinaba: mixin SimpleToHash; b993a8ad16 2010-11-24 kinaba: mixin SimpleCompareWithoutToHash; b97bd4f713 2010-11-23 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: unittest 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: class Temp 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: int x; 8de5b49cdf 2010-11-09 kinaba: string y; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleCompare; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: assert_eq( new Temp(1,"foo"), new Temp(1,"foo") ); 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash ); 8de5b49cdf 2010-11-09 kinaba: assert_ne( new Temp(1,"foo"), new Temp(2,"foo") ); 8de5b49cdf 2010-11-09 kinaba: assert_ne( new Temp(1,"foo"), new Temp(1,"bar") ); 8de5b49cdf 2010-11-09 kinaba: assert_gt( new Temp(1,"foo"), new Temp(1,"bar") ); 8de5b49cdf 2010-11-09 kinaba: assert_lt( new Temp(1,"foo"), new Temp(2,"bar") ); 8de5b49cdf 2010-11-09 kinaba: assert_ge( new Temp(1,"foo"), new Temp(1,"foo") ); 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: class TempDummy 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: int x; 8de5b49cdf 2010-11-09 kinaba: string y; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleCompare; 8de5b49cdf 2010-11-09 kinaba: } b993a8ad16 2010-11-24 kinaba: assert_ne( new Temp(1,"foo"), new TempDummy(1,"foo") ); b993a8ad16 2010-11-24 kinaba: assert_nothrow( new Temp(1,"foo") <= new TempDummy(1,"foo") ); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /// Mixing-in a simple toString method 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /*mixin*/ 8de5b49cdf 2010-11-09 kinaba: template SimpleToString() 8de5b49cdf 2010-11-09 kinaba: { 38fcc662be 2010-11-10 kinaba: /// member-by-member toString 8de5b49cdf 2010-11-09 kinaba: override string toString() 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: string str = sprintf!"%s("(typeof(this).stringof); 8de5b49cdf 2010-11-09 kinaba: foreach(i,mem; this.tupleof) 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: if(i) str ~= ","; 8de5b49cdf 2010-11-09 kinaba: static if( is(typeof(mem) == std.bigint.BigInt) ) 8de5b49cdf 2010-11-09 kinaba: str ~= std.bigint.toDecimalString(mem); 8de5b49cdf 2010-11-09 kinaba: else 8de5b49cdf 2010-11-09 kinaba: str ~= sprintf!"%s"(mem); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: return str ~ ")"; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: version(unittest) import std.bigint; 8de5b49cdf 2010-11-09 kinaba: unittest 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: class Temp 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: int x; 8de5b49cdf 2010-11-09 kinaba: string y; 8de5b49cdf 2010-11-09 kinaba: BigInt z; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleToString; 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" ); 8de5b49cdf 2010-11-09 kinaba: } 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /// Everything is in 8de5b49cdf 2010-11-09 kinaba: 8de5b49cdf 2010-11-09 kinaba: /*mixin*/ 8de5b49cdf 2010-11-09 kinaba: template SimpleClass() 8de5b49cdf 2010-11-09 kinaba: { 8de5b49cdf 2010-11-09 kinaba: mixin SimpleConstructor; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleCompare; 8de5b49cdf 2010-11-09 kinaba: mixin SimpleToString; 1c01f44f52 2010-11-13 kinaba: } 1c01f44f52 2010-11-13 kinaba: b993a8ad16 2010-11-24 kinaba: /// Utility 1c01f44f52 2010-11-13 kinaba: 6ac127ddd0 2010-11-23 kinaba: template firstParam(T) 1c01f44f52 2010-11-13 kinaba: { 6ac127ddd0 2010-11-23 kinaba: alias ParameterTypeTuple!(T)[0] firstParam; 8de5b49cdf 2010-11-09 kinaba: }