@@ -98,61 +98,94 @@ assert( !__traits(compiles, { class Tamp : Tomp { mixin SimpleConstructor; } }) ); } + +hash_t structuralHash(T)(T x) +{ + alias SC_Unqual!(T) UCT; + + static if(is(UCT == class)) + return (cast(UCT)x).toHash(); + else + static if(SC_HasGoodHash!(UCT)) + { return typeid(UCT).getHash(&x); } + else + static if(is(UCT T == T[])) + { hash_t h; foreach(e; x) h+=structuralHash(e); return h; } + else + static if(is(UCT == struct)) + static if(__traits(compiles, std.bigint.BigInt)) + static if(is(UCT == std.bigint.BigInt)) + return cast(hash_t) x.toInt(); + else + static assert(false, "should not use struct.toHash"); + else + static assert(false, "should not use struct.toHash"); + else + static assert(false, "nonhashable datatype "~UCT.stringof); +} + +alias std.traits.Unqual SC_Unqual; + +template SC_HasGoodHash(T) +{ + enum SC_HasGoodHash = + is(T : bool) || isNumeric!(T) || isSomeString!(T) || isSomeChar!(T) || isPointer!(T); +} /// Mixing-in the MOST-DERIVED-member-wise comparator for a class +/// BE SURE THAT THIS IS CONSISTENT WITH opCmp and opEquals template SimpleToHash() { override hash_t toHash() const /// member-by-member hash { hash_t h = 0; foreach(mem; this.tupleof) - h += typeid(mem).getHash(&mem); + h += structuralHash(mem); return h; } } /// Mixing-in the MOST-DERIVED-member-wise comparator for a class + +template SimpleCompareWithoutToHash() +{ + override bool opEquals(Object rhs) const /// member-by-member equality + { + return opCmp(rhs) == 0; + } + + override int opCmp(Object rhs_) const /// member-by-member compare + { + if( rhs_ is null ) + return -1; + if( auto rhs = cast(typeof(this))rhs_ ) + { + foreach(i,_; this.tupleof) + { + static if(is(typeof(_) == struct)) + auto c = (cast(SC_Unqual!(typeof(_)))this.tupleof[i]).opCmp(rhs.tupleof[i]); + else + auto c = typeid(_).compare(&this.tupleof[i],&rhs.tupleof[i]); + if(c) + return c; + } + return 0; + } + return typeid(this).opCmp(typeid(rhs_)); + } +} +/// Mixing-in the MOST-DERIVED-member-wise comparator for a class + /*mixin*/ template SimpleCompare() { - override bool opEquals(Object rhs_) const /// member-by-member equality - { - if( auto rhs = cast(typeof(this))rhs_ ) - { - foreach(i,_; this.tupleof) - if( this.tupleof[i] != (cast(const)rhs).tupleof[i] ) - return false; - return true; - } - assert(false, sprintf!"Cannot compare %s with %s"(typeid(this), typeid(rhs_))); - } - - mixin SimpleToHash; - - override int opCmp(Object rhs_) const /// member-by-member compare - { - if( auto rhs = cast(typeof(this))rhs_ ) - { - foreach(i,_; this.tupleof) - if( this.tupleof[i] != (cast(const)rhs).tupleof[i] ) { - auto a = (cast(SC_Unqual!(typeof(this)))this).tupleof[i]; - auto b = rhs.tupleof[i]; - return a < b ? -1 : +1; - } -// not work well for structures??????? -// 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_))); - } + mixin SimpleToHash; + mixin SimpleCompareWithoutToHash; } - -alias std.traits.Unqual SC_Unqual; unittest { class Temp @@ -176,10 +209,10 @@ 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") ); + assert_ne( new Temp(1,"foo"), new TempDummy(1,"foo") ); + assert_nothrow( new Temp(1,"foo") <= new TempDummy(1,"foo") ); } /// Mixing-in a simple toString method @@ -225,10 +258,10 @@ mixin SimpleCompare; mixin SimpleToString; } -/// Will be used for dynamic overload resolution pattern +/// Utility template firstParam(T) { alias ParameterTypeTuple!(T)[0] firstParam; }