Artifact Content
Not logged in

Artifact 004818c087feff6a3287f3cde422f80ffcdd6919


     1  /**
     2   * Authors: k.inaba
     3   * License: NYSL 0.9982 http://www.kmonos.net/nysl/
     4   *
     5   * Common tricks and utilities for programming in D.
     6   */
     7  module tricks.tricks;
     8  import tricks.test;
     9  import core.exception;
    10  import std.array  : appender;
    11  import std.format : formattedWrite;
    12  import std.traits;
    13  import std.typetuple;
    14  
    15  /// Simple Wrapper for std.format.doFormat
    16  
    17  string sprintf(string fmt, T...)(T params)
    18  {
    19  	auto writer = appender!string();
    20  	formattedWrite(writer, fmt, params);
    21  	return writer.data;
    22  }
    23  
    24  unittest
    25  {
    26  	assert_eq( sprintf!"%s == %04d"("1+2", 3), "1+2 == 0003" );
    27  	assert_eq( sprintf!"%2$s == %1$s"("1+2", 5, 8), "5 == 1+2" );
    28  	assert_throw!Error( sprintf!"%s%s"(1) );
    29  }
    30  
    31  /// Create an exception with automatically completed filename and lineno information
    32  
    33  ExceptionType genex(ExceptionType, string fn=__FILE__, int ln=__LINE__, T...)(T params)
    34  {
    35  	static if( T.length > 0 && is(T[$-1] : Throwable) )
    36  		return new ExceptionType(params[0..$-1], fn, ln, params[$-1]);
    37  	else
    38  		return new ExceptionType(params, fn, ln);
    39  }
    40  
    41  unittest
    42  {
    43  	assert_ne( genex!Exception("msg").file, "" );
    44  	assert_ne( genex!Exception("msg").line, 0 );
    45  	assert_ne( genex!Exception("msg",new Exception("bar")).next, Exception.init );
    46  }
    47  
    48  /// Mixing-in the bean constructor for a class
    49  
    50  /*mixin*/
    51  template SimpleConstructor()
    52  {
    53  	/// member-by-member constructor
    54  	static if( is(typeof(super) == Object) || super.tupleof.length==0 )
    55  		this( typeof(this.tupleof) params )
    56  		{
    57  			static if(this.tupleof.length>0)
    58  				this.tupleof = params;
    59  		}
    60  	else
    61  		this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
    62  		{
    63  			// including (only) the direct super class members
    64  			// may not always be a desirable choice, but should work for many cases
    65  			super(ps);
    66  			static if(this.tupleof.length>0)
    67  				this.tupleof = params;
    68  		}
    69  }
    70  
    71  unittest
    72  {
    73  	class Temp
    74  	{
    75  		int x;
    76  		string y;
    77  		mixin SimpleConstructor;
    78  	}
    79  	assert_eq( (new Temp(1,"foo")).x, 1 );
    80  	assert_eq( (new Temp(1,"foo")).y, "foo" );
    81  	assert( !__traits(compiles, new Temp) );
    82  	assert( !__traits(compiles, new Temp(1)) );
    83  	assert( !__traits(compiles, new Temp("foo",1)) );
    84  
    85  	class Tomp : Temp
    86  	{
    87  		real z;
    88  		mixin SimpleConstructor;
    89  	}
    90  	assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
    91  	assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
    92  	assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
    93  	assert( !__traits(compiles, new Tomp(3.14)) );
    94  
    95  	// shiyo- desu. Don't use in this way.
    96  	//   Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
    97  	//   but it fails because Tomp takes (int,string,real).
    98  	assert( !__traits(compiles, {
    99  		class Tamp : Tomp { mixin SimpleConstructor; }
   100  	}) );
   101  }
   102  
   103  /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
   104  
   105  /*mixin*/
   106  template SimpleCompare()
   107  {
   108  	override bool opEquals(Object rhs_) const /// member-by-member equality
   109  	{
   110  		if( auto rhs = cast(typeof(this))rhs_ )
   111  		{
   112  			foreach(i,_; this.tupleof)
   113  				if( this.tupleof[i] != (cast(const)rhs).tupleof[i] )
   114  					return false;
   115  			return true;
   116  		}
   117  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   118  	}
   119  
   120  	override hash_t toHash() const /// member-by-member hash
   121  	{
   122  		hash_t h = 0;
   123  		foreach(mem; this.tupleof)
   124  			h += typeid(mem).getHash(&mem);
   125  		return h;
   126  	}
   127  
   128  	override int opCmp(Object rhs_) const /// member-by-member compare
   129  	{
   130  		if( auto rhs = cast(typeof(this))rhs_ )
   131  		{
   132  			foreach(i,_; this.tupleof)
   133  				if( this.tupleof[i] != (cast(const)rhs).tupleof[i] ) {
   134  					auto a = (cast(SC_Unqual!(typeof(this)))this).tupleof[i];
   135  					auto b =  rhs.tupleof[i];
   136  					return a < b ? -1 : +1;
   137  				}
   138  // not work well for structures???????
   139  //				if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
   140  //					return c;
   141  			return 0;
   142  		}
   143  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   144  	}
   145  }
   146  
   147  alias std.traits.Unqual SC_Unqual;
   148  
   149  unittest
   150  {
   151  	class Temp
   152  	{
   153  		int x;
   154  		string y;
   155  		mixin SimpleConstructor;
   156  		mixin SimpleCompare;
   157  	}
   158  	assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
   159  	assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
   160  	assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
   161  	assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
   162  	assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
   163  	assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
   164  	assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
   165  
   166  	class TempDummy
   167  	{
   168  		int x;
   169  		string y;
   170  		mixin SimpleConstructor;
   171  		mixin SimpleCompare;
   172  	}
   173  	assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
   174  	assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
   175  }
   176  
   177  /// Mixing-in a simple toString method
   178  
   179  /*mixin*/
   180  template SimpleToString()
   181  {
   182  	/// member-by-member toString
   183  	override string toString()
   184  	{
   185  		string str = sprintf!"%s("(typeof(this).stringof);
   186  		foreach(i,mem; this.tupleof)
   187  		{
   188  			if(i) str ~= ",";
   189  			static if( is(typeof(mem) == std.bigint.BigInt) )
   190  				str ~= std.bigint.toDecimalString(mem);
   191  			else
   192  				str ~= sprintf!"%s"(mem);
   193  		}
   194  		return str ~ ")";
   195  	}
   196  }
   197  
   198  version(unittest) import std.bigint;
   199  unittest
   200  {
   201  	class Temp
   202  	{
   203  		int x;
   204  		string y;
   205  		BigInt z;
   206  		mixin SimpleConstructor;
   207  		mixin SimpleToString;
   208  	}
   209  	assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
   210  }
   211  
   212  /// Everything is in
   213  
   214  /*mixin*/
   215  template SimpleClass()
   216  {
   217  	mixin SimpleConstructor;
   218  	mixin SimpleCompare;
   219  	mixin SimpleToString;
   220  }
   221  
   222  /// Simple PatternMatcher
   223  
   224  /*mixin*/
   225  template SimplePatternMatch()
   226  {
   227  	SPM_Return!(PP) match(string fn=__FILE__, size_t ln=__LINE__, PP...)(PP pts)
   228  	{
   229  		foreach(i,_; pts)
   230  		{
   231  			alias pts[i] pt; // bug? pts[i]-->pt do not work
   232  			static if(__traits(compiles, SPM_isMatchTag(pt)))
   233  			{
   234  				if( auto v = cast(pt.dynamicType)this )
   235  					return pt(v.tupleof);
   236  			}
   237  			else
   238  			static if(__traits(compiles, SPM_isMatchAny(pt)))
   239  			{
   240  				return pt();
   241  			}
   242  			else
   243  			{
   244  				if( auto v = cast(SPM_PTT!(pt)[0])this )
   245  					return pt(v);
   246  			}
   247  		}
   248  		SPM_throwAssertError(fn, ln, "pattern matching failure");
   249  		assert(false);
   250  	}
   251  }
   252  
   253  /// Pattern case clause
   254  
   255  SPM_MatchTag!(T, fn) when(T, alias fn)()
   256  {
   257  	SPM_MatchTag!(T, fn) m;
   258  	return m;
   259  }
   260  
   261  /// Pattern case clause
   262  
   263  SPM_MatchAny!(fn) otherwise(alias fn)()
   264  {
   265  	SPM_MatchAny!(fn) m;
   266  	return m;
   267  }
   268  
   269  // implementation detail of SimplePatternMatch
   270  
   271  void SPM_throwAssertError(T...)(T t) { core.exception.onAssertErrorMsg(t); }
   272  
   273  struct SPM_MatchTag(T, alias fn)
   274  {
   275  	alias T dynamicType;
   276  	auto opCall(typeof(T.tupleof) s) { return fn(s); }
   277  }
   278  
   279  struct SPM_MatchAny(alias fn)
   280  {
   281  	auto opCall() { return fn(); }
   282  }
   283  
   284  template SPM_PTT(alias p)
   285  {
   286  	alias ParameterTypeTuple!(p) SPM_PTT;
   287  }
   288  
   289  template SPM_Each(P)
   290  {	
   291  	static if(__traits(compiles, SPM_isMatchTag(P.init)))
   292  		alias typeof(P(P.dynamicType.tupleof)) SPM_Each;
   293  	else
   294  	static if(__traits(compiles, SPM_isMatchAny(P.init)))
   295  		alias typeof(P()) SPM_Each;
   296  	else
   297  		alias ReturnType!(P) SPM_Each;
   298  }
   299  
   300  template SPM_aVoid(T:void, TS...) { alias SPM_aVoid!(TS) SPM_aVoid; }
   301  template SPM_aVoid(T, TS...) { alias TypeTuple!(T,SPM_aVoid!(TS)) SPM_aVoid; }
   302  template SPM_aVoid() { alias TypeTuple!() SPM_aVoid; }
   303  
   304  template SPM_Return(PP...)
   305  {
   306  	alias CommonType!(SPM_aVoid!(staticMap!(SPM_Each, PP))) SPM_Return;
   307  }
   308  
   309  void SPM_isMatchTag(T,alias fn)(SPM_MatchTag!(T,fn)){}
   310  void SPM_isMatchAny(alias fn)(SPM_MatchAny!(fn)){}
   311  
   312  unittest
   313  {
   314  	static abstract class Base {
   315  		mixin SimplePatternMatch;
   316  	}
   317  	class D1 : Base {
   318  		int x;
   319  		real y;
   320  		mixin SimpleConstructor;
   321  	}
   322  	class D2 : Base {
   323  		string s;
   324  		mixin SimpleConstructor;
   325  	}
   326  	class D3 : Base {
   327  		int[int] m;
   328  		mixin SimpleConstructor;
   329  	}
   330  
   331  	Base d1 = new D1(1, 2.3);
   332  	Base d2 = new D2("foobar");
   333  	Base d3 = new D3(null); (cast(D3)d3).m[1]=10;
   334  
   335  	// normal dispatch
   336  	assert_eq( d1.match(
   337  		(D1 x){return 1;},
   338  		(D2 x){return 2;}
   339  	), 1);
   340  	assert_eq( d2.match(
   341  		(D1 x){return 1;},
   342  		(D2 x){return 2;}
   343  	), 2);
   344  	assert_throw!AssertError( d3.match(
   345  		(D1 x){return 1;},
   346  		(D2 x){return 2;}
   347  	));
   348  	assert_eq( d3.match(
   349  		(D1 x){return 1;},
   350  		(D2 x){return 2;},
   351  		(Base x){return 3;}
   352  	), 3);
   353  	assert_eq( d2.match(
   354  		(D1 x){return 1;},
   355  		(D2 x){return 2;},
   356  		(Base x){return 3;}
   357  	), 2);
   358  	assert_eq( d2.match(
   359  		(D1 x){return 1;},
   360  		(Base x){return 3;},
   361  		(D2 x){return 2;}
   362  	), 3);
   363  
   364  	// member decomposing match
   365  	assert_eq( d1.match(
   366  		when!(D1, (x, y){return x + cast(int)y;}),
   367  		when!(D2, (x){return x.length;}),
   368  		when!(D3, (x){return x[1];})
   369  	), 3);
   370  	assert_eq( d2.match(
   371  		when!(D1, (x, y){return x + cast(int)y;}),
   372  		when!(D2, (x){return x.length;}),
   373  		when!(D3, (x){return x[1];})
   374  	), 6);
   375  	assert_eq( d3.match(
   376  		when!(D1, (x, y){return x + cast(int)y;}),
   377  		when!(D2, (x){return x.length;}),
   378  		when!(D3, (x){return x[1];})
   379  	), 10);
   380  	assert_throw!AssertError( d3.match(
   381  		when!(D1, (x, y){return x + cast(int)y;}),
   382  		when!(D2, (x){return x.length;})
   383  	));
   384  	assert_eq( d2.match(
   385  		when!(D1, (x, y){return x + cast(int)y;}),
   386  		when!(D2, (x){return x.length;}),
   387  		otherwise!({return 999;})
   388  	), 6);
   389  	assert_eq( d2.match(
   390  		when!(D1, (x, y){return x + cast(int)y;}),
   391  		otherwise!({return 999;}),
   392  		when!(D2, (x){return x.length;})
   393  	), 999);
   394  }
   395  
   396  /// Will be used for dynamic overload resolution pattern
   397  
   398  template firstParam(T)
   399  {
   400  	alias ParameterTypeTuple!(T)[0] firstParam;
   401  }