Array toHash()

2014-11-26 Thread David Held via Digitalmars-d-learn
I have a class which contains an int[] and some other stuff.  I want to 
use my class as the key for an AA, so I am overriding toHash().  But the 
int[] is the only part which should produce the hash code.  I know that 
int[].toHash() is defined somehow, because I can put int[] directly into 
an AA without writing any hash functions.  But I don't know how to spell 
this explicitly or force the compiler to generate it for me so that I 
can forward to it in my toHash().  For illustration:


class Foo
{
override
size_t toHash() @trusted pure const nothrow
{
// error: no property 'toHash' for type 'int[]'
return importantStuff.toHash();
}

// override opEquals() too...

int[] importantStuff;
bool  notImportant;
int   ignoreMe;
}

Any way to avoid re-implementing this hash function?

Dave


Re: Array toHash()

2014-11-26 Thread Ali Çehreli via Digitalmars-d-learn

On 11/26/2014 04:25 PM, David Held wrote:

> class Foo
> {
>  override
>  size_t toHash() @trusted pure const nothrow
>  {
>  // error: no property 'toHash' for type 'int[]'
>  return importantStuff.toHash();
>  }

The getHash() member function of the particular TypeInfo can be used. 
However, it is not currently pure, so you must comment that out from 
your toHash:


override
size_t toHash() @trusted /* pure */ const nothrow
{
return typeid(importantStuff).getHash(&importantStuff);

}

If a function can safely be casted to pure, you can use the following 
yet-missing-in-phobos function template:


import std.traits;

auto assumePure(T)(T t)
if (isFunctionPointer!T || isDelegate!T)
{
enum attrs = functionAttributes!T | FunctionAttribute.pure_;
return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
}

// ...

override
size_t toHash() @trusted pure const nothrow
{
auto func = assumePure(&(typeid(importantStuff).getHash));
return func(&importantStuff);
}

Note that now your toHash can be pure.

Ali



Re: Array toHash()

2014-11-29 Thread David Held via Digitalmars-d-learn

On 11/26/2014 4:40 PM, Ali Çehreli wrote:

[...]
 override
 size_t toHash() @trusted pure const nothrow
 {
 auto func = assumePure(&(typeid(importantStuff).getHash));
 return func(&importantStuff);
 }


Very helpful, thanks!  Am I right in assuming that there is some runtime 
cost here, as we are calling through a function pointer?  Is the lack of 
inlining the only cost, or is getting at the typeinfo also costly?


Dave




Re: Array toHash()

2014-11-29 Thread Ali Çehreli via Digitalmars-d-learn

On 11/29/2014 12:45 PM, David Held wrote:

> On 11/26/2014 4:40 PM, Ali Çehreli wrote:
>> [...]
>>  override
>>  size_t toHash() @trusted pure const nothrow
>>  {
>>  auto func = assumePure(&(typeid(importantStuff).getHash));
>>  return func(&importantStuff);
>>  }
>

> Am I right in assuming that there is some runtime
> cost here, as we are calling through a function pointer?

I think so. However, I think the compiler can eliminate dereferencing 
the function pointer if it can prove that it is not necessary.


> Is the lack of inlining the only cost, or is getting at the
> typeinfo also costly?

typeid() is a runtime function. I think it will be costly every time 
toHash is called. The function pointer can be initialized once.


// I've "deduced" the type from an error message. ;)
static const ulong delegate(const(void*)) const pure nothrow 
@trusted func;


// This will be executed once per thread:
static this() {
func = assumePure(&(typeid(Foo.init.importantStuff).getHash));
}

override
size_t toHash() @trusted pure const nothrow
{
return func(&importantStuff);
}

Now there is no TypeInfo lookup after thread initialization time.

Ali



Re: Array toHash()

2014-11-29 Thread David Held via Digitalmars-d-learn

On 11/29/2014 3:59 PM, Ali Çehreli wrote:

[...]
typeid() is a runtime function. I think it will be costly every time
toHash is called. The function pointer can be initialized once.

 // I've "deduced" the type from an error message. ;)
 static const ulong delegate(const(void*)) const pure nothrow
@trusted func;

 // This will be executed once per thread:
 static this() {
 func = assumePure(&(typeid(Foo.init.importantStuff).getHash));
 }

 override
 size_t toHash() @trusted pure const nothrow
 {
 return func(&importantStuff);
 }

Now there is no TypeInfo lookup after thread initialization time.


Nice!  This is getting very fancy, but also a bit unfortunate.  It would 
be appropriate if the type of importantStuff were arbitrary, but it's 
not.  It's int[].  In fact, the fact that I can get a compiler-generated 
hash function for any type (I presume) surprises me.  Not entirely, 
since the compiler can just treat types as binary blobs, and define a 
hash function that operates as if it were byte[].


Although it is nice that we can get at stuff through reflection, this is 
not quite the same as the ostensibly static [and inlinable] call to 
int[].toHash() which logically occurs when int[] is used directly as a key.


Also, would it be possible to avoid spelling the delegate type by making 
func a function static and using an auto return on a named method?


Finally, I find it a little surprising that there's no standard module 
which implements a quality hash function builder (using something like 
MurmurHash or CityHash).  This seems like it would be a fairly common 
requirement for a lot of users.  Are there any non-standard libraries 
which do this?


Dave