Thread overview
Can classes be used as associative array keys?
Oct 10, 2004
Eric Hanchrow
Oct 10, 2004
Sjoerd van Leent
Oct 10, 2004
Regan Heath
Oct 10, 2004
Eric Hanchrow
Oct 10, 2004
Regan Heath
Oct 10, 2004
Derek Parnell
Oct 11, 2004
Stewart Gordon
October 10, 2004
The documentation says they can, but the following code fails the second assertion:

========================================
    class thing
    {
      int x;
      this (int i) { x = i; }
    }

    int main ()
    {
      thing a = new thing (123);
      thing b = new thing (123);
      int[thing] map1;
      map1[a] = 0;
      map1[b] = 1;

      int[char[]] map2;
      map2["123"] = 0;
      map2["123"] = 1;

      assert (1 == map2.length);
      assert (1 == map1.length);

      return 0;
    }
========================================

Perhaps I need to write a comparison or hashing member for my class, but the documentation doesn't describe it.  Any ideas how I can get this to work?

-- 
            |\      _,,,---,,_
      ZZZzz /,`.-'`'    -.  ;-;;,_
           |,4-  ) )-,_. ,\ (  `'-'
          '---''(_/--'  `-'\_) fL
October 10, 2004
Eric Hanchrow wrote:
> The documentation says they can, but the following code fails the
> second assertion:
> 
> ========================================
>     class thing
>     {
>       int x;
>       this (int i) { x = i; }
>     }
> 
>     int main ()
>     {
>       thing a = new thing (123);
>       thing b = new thing (123);
>       int[thing] map1;
>       map1[a] = 0;
>       map1[b] = 1;
> 
>       int[char[]] map2;
>       map2["123"] = 0;
>       map2["123"] = 1;
> 
>       assert (1 == map2.length);
>       assert (1 == map1.length);
> 
>       return 0;
>     }
> ========================================
> 
> Perhaps I need to write a comparison or hashing member for my class,
> but the documentation doesn't describe it.  Any ideas how I can get
> this to work?
> 

Classes are standard by reference. So you have two new "things" made, which are not the same. These are added to map1 as a and b. So there are logically two items within the map.

For char[] it is different. You are using it a char as array, which in contrast to a class *does* point to the same item. If you would want to let "thing a" and "thing b" be the same, you should let b point to a, such as:

thing a = new thing(123);
thing b = a;

this would work as you wanted.

Regards,
Sjoerd
October 10, 2004
On Sun, 10 Oct 2004 11:45:51 -0700, Eric Hanchrow <offby1@blarg.net> wrote:
> The documentation says they can, but the following code fails the
> second assertion:
>
> ========================================
>     class thing
>     {
>       int x;
>       this (int i) { x = i; }
>     }
>
>     int main ()
>     {
>       thing a = new thing (123);
>       thing b = new thing (123);
>       int[thing] map1;
>       map1[a] = 0;
>       map1[b] = 1;
>
>       int[char[]] map2;
>       map2["123"] = 0;
>       map2["123"] = 1;
>
>       assert (1 == map2.length);
>       assert (1 == map1.length);
>
>       return 0;
>     }
> ========================================
>
> Perhaps I need to write a comparison or hashing member for my class,
> but the documentation doesn't describe it.  Any ideas how I can get
> this to work?

Sjoerd is right about the problem with your example above, 'a' and 'b' above do not compare equal because they are references to different blocks of memory. The default comparrison compares memory location.

I thought you could define your own opCmp or opEquals operator in order to make the associative array realise that you intend 'a' and 'b' to be 'the same' but all my attempts have failed...

class thing
{
  int x;
  this (int i) { x = i; }
  int opCmp(thing rhs)
  {
  	return x-rhs.x;
  }
  int opEquals(thing rhs)
  {
  	return x==rhs.x;
  }
}

perhaps someone else can help us both?

Regan

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
October 10, 2004
>>>>> "Regan" == Regan Heath writes:

    Regan> I thought you could define your own opCmp or opEquals
    Regan> operator in order to make the associative array realise
    Regan> that you intend 'a' and b' to be 'the same' but all my
    Regan> attempts have failed...

Same here.  I'm sure there's a way to do it, or at least, that Walter
intends there eventually to be such a way.

-- 
Yahoo uses Javascript in a few places, though not many.  I once asked someone there how this worked out, and he said "they ended up learning a lot about different browser versions."

    --Paul Graham (http://www.paulgraham.com/road.html)
October 10, 2004
On Sun, 10 Oct 2004 15:47:13 -0700, Eric Hanchrow <offby1@blarg.net> wrote:

>>>>>> "Regan" == Regan Heath writes:
>
>     Regan> I thought you could define your own opCmp or opEquals
>     Regan> operator in order to make the associative array realise
>     Regan> that you intend 'a' and b' to be 'the same' but all my
>     Regan> attempts have failed...
>
> Same here.  I'm sure there's a way to do it, or at least, that Walter
> intends there eventually to be such a way.

I found (after looking in \dmd\src\phobos\internal\object.d) the 'uint toHash()' method, adding one like so:

class thing
{
  int x;
  this (int i) { x = i; }
  int opCmp(Object p)
  {
  	thing rhs = cast(thing)p;
  	printf("opCmp %d,%d\n",x,rhs.x);
  	return x-rhs.x;
  }
  int opEquals(Object p)
  {
  	thing rhs = cast(thing)p;
  	printf("opEquals %d,%d\n",x,rhs.x);
  	return x==rhs.x;
  }
  uint toHash()
  {
  	printf("toHash %d\n",x);
  	return x;
  }
}

makes it work as desired :)

Notice I had to change "opCmp(thing .." to "opCmp(Object .." as well. It would be nice if that was not necessary, after all, the AA does know what type is involved.

Perhaps that would require a cast internally so isn't done for efficiency reasons?

Perhaps it's done so as you have to handle it being compared to all objects, if that is the case, how do you return not comparable?

eg.

int opCmp(Object o)
{
  Foo f;
  Bar b;

  f = cast(Foo)o;
  if (f) return compare_with_foo();

  b = cast(Bar)o;
  if (b) return compare_with_bar();

  //how do we return 'not comparable?'
  return 0;
}

Regan

-- 
Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
October 10, 2004
On Mon, 11 Oct 2004 12:24:49 +1300, Regan Heath wrote:


[snip]

>    //how do we return 'not comparable?'

Throw an exception, maybe?

-- 
Derek
Melbourne, Australia
11/10/2004 9:37:18 AM
October 11, 2004
Regan Heath wrote:

<snip>
> Perhaps it's done so as you have to handle it being compared to all objects, if that is the case, how do you return not comparable?
<snip>

You can usually get away with assuming that the RHS is of compatible type.  If the cast fails, it will be a null reference and cause an AV. Of course, if you've got mutually comparable classes to be considered separately, that's another matter....

But several of us agree that it's on the stupid side.

http://www.digitalmars.com/drn-bin/wwwnews?D/26144
http://www.digitalmars.com/drn-bin/wwwnews?digitalmars.D/10558

Stewart.