Artifact Content
Not logged in

Artifact 73906cba073bce6cf3d9a4b411a3e6bb78719468


     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  /*mixin*/
   121  template SimpleConstructor()
   122  {
   123  	static if( is(typeof(super) == Object) || super.tupleof.length==0 )
   124  		this( typeof(this.tupleof) params )
   125  		{
   126  			static if(this.tupleof.length>0)
   127  				this.tupleof = params;
   128  		}
   129  	else
   130  		this( typeof(super.tupleof) ps, typeof(this.tupleof) params )
   131  		{
   132  			// including (only) the direct super class members
   133  			// may not always be a desirable choice, but should work for many cases
   134  			super(ps);
   135  			static if(this.tupleof.length>0)
   136  				this.tupleof = params;
   137  		}
   138  }
   139  
   140  unittest
   141  {
   142  	class Temp
   143  	{
   144  		int x;
   145  		string y;
   146  		mixin SimpleConstructor;
   147  	}
   148  	assert_eq( (new Temp(1,"foo")).x, 1 );
   149  	assert_eq( (new Temp(1,"foo")).y, "foo" );
   150  	assert( !__traits(compiles, new Temp) );
   151  	assert( !__traits(compiles, new Temp(1)) );
   152  	assert( !__traits(compiles, new Temp("foo",1)) );
   153  
   154  	class Tomp : Temp
   155  	{
   156  		real z;
   157  		mixin SimpleConstructor;
   158  	}
   159  	assert_eq( (new Tomp(1,"foo",2.5)).x, 1 );
   160  	assert_eq( (new Tomp(1,"foo",2.5)).y, "foo" );
   161  	assert_eq( (new Tomp(1,"foo",2.5)).z, 2.5 );
   162  	assert( !__traits(compiles, new Tomp(3.14)) );
   163  
   164  	// shiyo- desu. Don't use in this way.
   165  	//   Tamp tries to call new Tomp(real) (because it only sees Tomp's members),
   166  	//   but it fails because Tomp takes (int,string,real).
   167  	assert( !__traits(compiles, {
   168  		class Tamp : Tomp
   169  		{
   170  			mixin SimpleConstructor;
   171  		}
   172  	}) );
   173  }
   174  
   175  /// Mixing-in the MOST-DERIVED-member-wise comparator for a class
   176  
   177  /*mixin*/
   178  template SimpleCompare()
   179  {
   180  	override bool opEquals(Object rhs_) const
   181  	{
   182  		if( auto rhs = cast(typeof(this))rhs_ )
   183  		{
   184  			foreach(i,_; this.tupleof)
   185  				if( this.tupleof[i] != rhs.tupleof[i] )
   186  					return false;
   187  			return true;
   188  		}
   189  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   190  	}
   191  
   192  	override hash_t toHash() const
   193  	{
   194  		hash_t h = 0;
   195  		foreach(mem; this.tupleof)
   196  			h += typeid(mem).getHash(&mem);
   197  		return h;
   198  	}
   199  
   200  	override int opCmp(Object rhs_) const
   201  	{
   202  		if( auto rhs = cast(typeof(this))rhs_ )
   203  		{
   204  			foreach(i,_; this.tupleof)
   205  				if(auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]))
   206  					return c;
   207  			return 0;
   208  		}
   209  		assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_)));
   210  	}
   211  }
   212  
   213  unittest
   214  {
   215  	class Temp
   216  	{
   217  		int x;
   218  		string y;
   219  		mixin SimpleConstructor;
   220  		mixin SimpleCompare;
   221  	}
   222  	assert_eq( new Temp(1,"foo"), new Temp(1,"foo") );
   223  	assert_eq( (new Temp(1,"foo")).toHash, (new Temp(1,"foo")).toHash );
   224  	assert_ne( new Temp(1,"foo"), new Temp(2,"foo") );
   225  	assert_ne( new Temp(1,"foo"), new Temp(1,"bar") );
   226  	assert_gt( new Temp(1,"foo"), new Temp(1,"bar") );
   227  	assert_lt( new Temp(1,"foo"), new Temp(2,"bar") );
   228  	assert_ge( new Temp(1,"foo"), new Temp(1,"foo") );
   229  
   230  	class TempDummy
   231  	{
   232  		int x;
   233  		string y;
   234  		mixin SimpleConstructor;
   235  		mixin SimpleCompare;
   236  	}
   237  	assert_throw!AssertError( new Temp(1,"foo") == new TempDummy(1,"foo") );
   238  	assert_throw!AssertError( new Temp(1,"foo") <= new TempDummy(1,"foo") );
   239  }
   240  
   241  /// Mixing-in a simple toString method
   242  
   243  /*mixin*/
   244  template SimpleToString()
   245  {
   246  	override string toString()
   247  	{
   248  		string str = sprintf!"%s("(typeof(this).stringof);
   249  		foreach(i,mem; this.tupleof)
   250  		{
   251  			if(i) str ~= ",";
   252  			static if( is(typeof(mem) == std.bigint.BigInt) )
   253  				str ~= std.bigint.toDecimalString(mem);
   254  			else
   255  				str ~= sprintf!"%s"(mem);
   256  		}
   257  		return str ~ ")";
   258  	}
   259  }
   260  
   261  version(unittest) import std.bigint;
   262  unittest
   263  {
   264  	class Temp
   265  	{
   266  		int x;
   267  		string y;
   268  		BigInt z;
   269  		mixin SimpleConstructor;
   270  		mixin SimpleToString;
   271  	}
   272  	assert_eq( (new Temp(1,"foo",BigInt(42))).toString(), "Temp(1,foo,42)" );
   273  }
   274  
   275  /// Everything is in
   276  
   277  /*mixin*/
   278  template SimpleClass()
   279  {
   280  	mixin SimpleConstructor;
   281  	mixin SimpleCompare;
   282  	mixin SimpleToString;
   283  }