Artifact Content
Not logged in

Artifact e911a30d1f923777050ab4ecacb5553a689ce658


     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 polemy.tricks;
     8  import std.array      : appender;
     9  import std.format     : formattedWrite;
    10  import core.exception : onAssertErrorMsg, AssertError;
    11  
    12  /// Simple Wrapper for std.format.doFormat
    13  
    14  string sprintf(string fmt, T...)(T params)
    15  {
    16  	auto writer = appender!string();
    17  	formattedWrite(writer, fmt, params);
    18  	return writer.data;
    19  }
    20  
    21  unittest
    22  {
    23  	assert( sprintf!"%s == %d"("1+2", 3)   == "1+2 == 3" );
    24  	assert( sprintf!"%s == %04d"("1+2", 3) == "1+2 == 0003" );
    25  }
    26  
    27  /// Unittest helper that asserts an expression must throw something
    28  
    29  void assert_throw(ExceptionType, T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
    30  {
    31  	try {
    32  		t();
    33  	} catch(ExceptionType) {
    34  		return;
    35  	} catch(Throwable e) {
    36  		onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
    37  	}
    38  	onAssertErrorMsg(fn, ln, msg.length ? msg : "no execption");
    39  }
    40  
    41  /// Unittest helper that asserts an expression must not throw anything
    42  
    43  void assert_nothrow(T, string fn=__FILE__, int ln=__LINE__)(lazy T t, string msg="")
    44  {
    45  	try {
    46  		t();
    47  	} catch(Throwable e) {
    48  		onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
    49  	}
    50  }
    51  
    52  unittest
    53  {
    54  	auto error = {throw new Error("hello");};
    55  	auto nothing = (){};
    56  	auto assertError = {assert(0);};
    57  
    58  	assert_nothrow          ( assert_nothrow(nothing()) );
    59  	assert_throw!AssertError( assert_nothrow(error()) );
    60  	assert_throw!AssertError( assert_nothrow(assertError()) );
    61  
    62  	assert_nothrow          ( assert_throw!Error(error()) );
    63  	assert_throw!AssertError( assert_throw!Error(nothing()) );
    64  	assert_nothrow          ( assert_throw!Error(assertError()) );
    65  	assert_throw!AssertError( assert_throw!AssertError(error()) );
    66  }
    67  
    68  /// Unittest helpers asserting two values are in some relation ==, !=, <, <=, >, >=
    69  
    70  template assertOp(string op)
    71  {
    72  	void assertOp(A, B, string fn=__FILE__, int ln=__LINE__)(A a, B b, string msg="")
    73  	{
    74  		try {
    75  			if( mixin("a"~op~"b") ) return;
    76  		} catch(Throwable e) {
    77  			onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"exception [%s]"(e));
    78  		}
    79  		onAssertErrorMsg(fn, ln, msg.length ? msg : sprintf!"%s !%s %s"(a,op,b));
    80  	}
    81  }
    82  
    83  alias assertOp!(`==`) assert_eq;
    84  alias assertOp!(`!=`) assert_ne;
    85  alias assertOp!(`<`)  assert_lt;
    86  alias assertOp!(`<=`) assert_le;
    87  alias assertOp!(`>`)  assert_gt;
    88  alias assertOp!(`>=`) assert_ge;
    89  
    90  unittest
    91  {
    92  	assert_nothrow( assert_eq(1, 1) );
    93  	assert_nothrow( assert_ne(1, 0) );
    94  	assert_nothrow( assert_lt(0, 1) );
    95  	assert_nothrow( assert_le(0, 1) );
    96  	assert_nothrow( assert_le(0, 0) );
    97  	assert_nothrow( assert_gt(1, 0) );
    98  	assert_nothrow( assert_ge(1, 0) );
    99  	assert_nothrow( assert_ge(0, 0) );
   100  
   101  	assert_throw!AssertError( assert_eq(1, 0) );
   102  	assert_throw!AssertError( assert_ne(1, 1) );
   103  	assert_throw!AssertError( assert_lt(1, 1) );
   104  	assert_throw!AssertError( assert_lt(1, 0) );
   105  	assert_throw!AssertError( assert_le(1, 0) );
   106  	assert_throw!AssertError( assert_gt(0, 0) );
   107  	assert_throw!AssertError( assert_gt(0, 1) );
   108  	assert_throw!AssertError( assert_ge(0, 1) );
   109  
   110  	class Temp { bool opEquals(int x){return x/x==x;} }
   111  	assert_throw!AssertError( assert_eq(new Temp, 0) );
   112  	assert_nothrow          ( assert_eq(new Temp, 1) );
   113  	assert_throw!AssertError( assert_eq(new Temp, 2) );
   114  }
   115  
   116  /* [Todo] is there any way to clearnly implement "assert_compiles" and "assert_not_compile"? */
   117  
   118  /// Mixing-in the bean constructor for a class
   119  
   120  template SimpleConstructor()
   121  {
   122  	static if( is(typeof(super) == Object) || super.tupleof.length==0 )
   123  		this( typeof(this.tupleof) params )
   124  		{
   125  			static if(this.tupleof.length>0)
   126  				this.tupleof = params;
   127  		}
   128  	else
   129  		this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
   130  		{
   131  			// including (only) the direct super class members
   132  			// may not always be a desirable choice, but should work for many cases
   133  			super(ps);
   134  			static if(this.tupleof.length>0)
   135  				this.tupleof = params;
   136  		}
   137  }
   138  
   139  unittest
   140  {
   141  	class Temp
   142  	{
   143  		int x;
   144  		string y;
   145  		mixin SimpleConstructor;
   146  	}
   147  	assert_eq( (new Temp(1,"foo")).x, 1 );
   148  	assert_eq( (new Temp(1,"foo")).y, "foo" );
   149  	assert( !__traits(compiles, new Temp) );
   150  	assert( !__traits(compiles, new Temp(1)) );
   151  	assert( !__traits(compiles, new Temp("foo",1)) );
   152  
   153  	class Tomp : Temp
   154  	{
   155  		real z;
   156  		mixin SimpleConstructor;
   157  	}
   158  	assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
   159  	assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
   160  	assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
   161  	assert( !__traits(compiles, new Tomp(3.14)) );
   162  
   163  	// shiyo- desu. Don't use in this way.
   164  	//   Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
   165  	//   but it fails because Tomp takes (int,string,real).
   166  	assert( !__traits(compiles, {
   167  		class Tamp : Tomp
   168  		{
   169  			mixin SimpleConstructor;
   170  		}
   171  	}) );
   172  }
   173  
   174  /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
   175  
   176  template SimpleCompare()
   177  {
   178  	override bool opEquals(Object rhs_) const
   179  	{
   180  		if( auto rhs = cast(typeof(this))rhs_ )
   181  		{
   182  			foreach(i,_; this.tupleof)
   183  				if( this.tupleof[i] != rhs.tupleof[i] )
   184  					return false;
   185  			return true;
   186  		}
   187  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   188  	}
   189  
   190  	override hash_t toHash() const
   191  	{
   192  		hash_t h = 0;
   193  		foreach(mem; this.tupleof)
   194  			h += typeid(mem).getHash(&mem);
   195  		return h;
   196  	}
   197  
   198  	override int opCmp(Object rhs_) const
   199  	{
   200  		if( auto rhs = cast(typeof(this))rhs_ )
   201  		{
   202  			foreach(i,_; this.tupleof)
   203  				if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
   204  					return c;
   205  			return 0;
   206  		}
   207  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   208  	}
   209  }
   210  
   211  unittest
   212  {
   213  	class Temp
   214  	{
   215  		int x;
   216  		string y;
   217  		mixin SimpleConstructor;
   218  		mixin SimpleCompare;
   219  	}
   220  	assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
   221  	assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
   222  	assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
   223  	assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
   224  	assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
   225  	assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
   226  	assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
   227  
   228  	class TempDummy
   229  	{
   230  		int x;
   231  		string y;
   232  		mixin SimpleConstructor;
   233  		mixin SimpleCompare;
   234  	}
   235  	assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
   236  	assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
   237  }