www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Confused about class equality

reply strtr <strtr spam.com> writes:
The program below outputs, as I would expect :
Same Value.
Same Object.
3 : 44E15C 0000
3 : 44E15C 0000
5 : 44E15C 0000
5 : 44E15C 0000

Now what would it mean if it were to output :
Same Value.
3 : 5B536C 59D020
3 : 59CE0C 59CEF0
5 : 5B536C 59D020
5 : 59CE0C 59CEF0
(Output from essentially the same piece of code within a larger project)

Not the same object, but still able to change it with one call.
What is it I don't get?

------
module main;

import std.stdio;
import std.string;

interface I{
	char[] toString();
	int value();
	void value(int v);
}

class C : I{
	private int _value;
	
	this(int v){ _value = v; }
	char[] toString(){ return format(_value); };
	int value(){ return _value; };
	void value(int v){ _value = v; };
}
private I[3][3] arr;

public I getI( int x, int y){ return arr[x][y]; }
public void setI( int x, int y, I i ){ arr[x][y]= i; }

void main(){
	C c = new C(3);
	setI( 0, 0, c );
	setI( 1, 2, c );
	I i1 = null;
	I i2 = null;
	i1 = getI(0,0);
	i2 = getI(1,2);
	
	if( i1 !is null && i2 !is null && i2.value == i1.value ) {
		writefln("Same Value.");
		if( i2 is i1 ) writefln("Same Object.");
		writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
		writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
		i1.value = 5;
		writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
		writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
	}
}
Apr 02 2010
next sibling parent reply Justin Spahr-Summers <Justin.SpahrSummers gmail.com> writes:
On Fri, 02 Apr 2010 22:36:40 -0400, strtr <strtr spam.com> wrote:
 
 The program below outputs, as I would expect :
 Same Value.
 Same Object.
 3 : 44E15C 0000
 3 : 44E15C 0000
 5 : 44E15C 0000
 5 : 44E15C 0000
 
 Now what would it mean if it were to output :
 Same Value.
 3 : 5B536C 59D020
 3 : 59CE0C 59CEF0
 5 : 5B536C 59D020
 5 : 59CE0C 59CEF0
 (Output from essentially the same piece of code within a larger project)
 
 Not the same object, but still able to change it with one call.
 What is it I don't get?
 
 ------
 module main;
 
 import std.stdio;
 import std.string;
 
 interface I{
 	char[] toString();
 	int value();
 	void value(int v);
 }
 
 class C : I{
 	private int _value;
 	
 	this(int v){ _value = v; }
 	char[] toString(){ return format(_value); };
 	int value(){ return _value; };
 	void value(int v){ _value = v; };
 }
 private I[3][3] arr;
 
 public I getI( int x, int y){ return arr[x][y]; }
 public void setI( int x, int y, I i ){ arr[x][y]= i; }
 
 void main(){
 	C c = new C(3);
 	setI( 0, 0, c );
 	setI( 1, 2, c );
 	I i1 = null;

You may want to try reducing the code within your "larger project" to the smallest use case where the problem still occurs, because, I agree, the code as written will always result in the same object, since you allocate only one.
Apr 02 2010
parent strtr <strtr spam.com> writes:
Justin Spahr-Summers Wrote:

 On Fri, 02 Apr 2010 22:36:40 -0400, strtr <strtr spam.com> wrote:
 
 You may want to try reducing the code within your "larger project" to 
 the smallest use case where the problem still occurs, because, I agree, 
 the code as written will always result in the same object, since you 
 allocate only one.

The main starting from "I i1 = null;" is actual code from my program. The getI function is also the same and no code resides in between, nor is it multi-threaded. Within this setting, how can you manipulate the objects in arr(which is larger in the actual code) to get the strange output?
Apr 02 2010
prev sibling next sibling parent reply strtr <strtr spam.com> writes:
What I probably mean to ask is :
In the code below, for what kind of i1 and i2 would the output be like this :
---------
Same Value.
3 : 5B536C 59D020
3 : 59CE0C 59CEF0
5 : 5B536C 59D020
5 : 59CE0C 59CEF0
---------
if( i1 !is null && i2 !is null && i2.value == i1.value ) {
	writefln("Same Value.");
	if( i2 is i1 ) writefln("Same Object.");
	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
	i1.value = 5;
	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
}
Apr 02 2010
parent reply Jacob Carlborg <doob me.com> writes:
On 4/3/10 07:03, strtr wrote:
 What I probably mean to ask is :
 In the code below, for what kind of i1 and i2 would the output be like this :
 ---------
 Same Value.
 3 : 5B536C 59D020
 3 : 59CE0C 59CEF0
 5 : 5B536C 59D020
 5 : 59CE0C 59CEF0
 ---------
 if( i1 !is null&&  i2 !is null&&  i2.value == i1.value ) {
 	writefln("Same Value.");
 	if( i2 is i1 ) writefln("Same Object.");
 	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 	i1.value = 5;
 	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 }

I would guess: i1 = new I; i2 = new I; They are two different instance of "I" but containing the same value. When comparing two objects using "is", the addresses are compared and not the data in the object, i.e. instance variables.
Apr 03 2010
parent strtr <strtr spam.com> writes:
Jacob Carlborg Wrote:

 On 4/3/10 07:03, strtr wrote:
 What I probably mean to ask is :
 In the code below, for what kind of i1 and i2 would the output be like this :
 ---------
 Same Value.
 3 : 5B536C 59D020
 3 : 59CE0C 59CEF0
 5 : 5B536C 59D020
 5 : 59CE0C 59CEF0
 ---------
 if( i1 !is null&&  i2 !is null&&  i2.value == i1.value ) {
 	writefln("Same Value.");
 	if( i2 is i1 ) writefln("Same Object.");
 	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 	i1.value = 5;
 	writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 	writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 }

I would guess: i1 = new I; i2 = new I; They are two different instance of "I" but containing the same value. When comparing two objects using "is", the addresses are compared and not the data in the object, i.e. instance variables.

Wouldn't that simply result in the following? 3 : 5B536C 59D020 3 : 59CE0C 59CEF0 5 : 5B536C 59D020 3 : 59CE0C 59CEF0
Apr 03 2010
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
strtr wrote:
 The program below outputs, as I would expect :
 Same Value.
 Same Object.
 3 : 44E15C 0000
 3 : 44E15C 0000
 5 : 44E15C 0000
 5 : 44E15C 0000
 
 Now what would it mean if it were to output :
 Same Value.
 3 : 5B536C 59D020
 3 : 59CE0C 59CEF0
 5 : 5B536C 59D020
 5 : 59CE0C 59CEF0
 (Output from essentially the same piece of code within a larger project)
 
 Not the same object, but still able to change it with one call.
 What is it I don't get?
 
 ------
 module main;
 
 import std.stdio;
 import std.string;
 
 interface I{
 	char[] toString();
 	int value();
 	void value(int v);
 }
 
 class C : I{
 	private int _value;
 	
 	this(int v){ _value = v; }
 	char[] toString(){ return format(_value); };
 	int value(){ return _value; };
 	void value(int v){ _value = v; };
 }
 private I[3][3] arr;
 
 public I getI( int x, int y){ return arr[x][y]; }
 public void setI( int x, int y, I i ){ arr[x][y]= i; }
 
 void main(){
 	C c = new C(3);
 	setI( 0, 0, c );
 	setI( 1, 2, c );
 	I i1 = null;
 	I i2 = null;
 	i1 = getI(0,0);
 	i2 = getI(1,2);
 	
 	if( i1 !is null && i2 !is null && i2.value == i1.value ) {
 		writefln("Same Value.");
 		if( i2 is i1 ) writefln("Same Object.");
 		writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 		writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 		i1.value = 5;
 		writefln( i2.toString()," : ",i2.__vptr," ",i2.__monitor);
 		writefln( i1.toString(), " : ", i1.__vptr," ",i1.__monitor);
 	}
 }

The code works as expected with 2.042 I had to modify the toString() functions to return string, and say "override" in C's toString definition; and had to modify the writefln() calls: writefln("%s : %s %s", i2.toString(), i2.__vptr, i2.__monitor); The output: Same Value. Same Object. 3 : 806D3F4 0 3 : 806D3F4 0 5 : 806D3F4 0 5 : 806D3F4 0 Ali
Apr 03 2010
parent reply strtr <strtr spam.com> writes:
Ali Çehreli Wrote:
 
 The code works as expected with 2.042
 
 I had to modify the toString() functions to return string, and say 
 "override" in C's toString definition; and had to modify the writefln() 
 calls:
 
    writefln("%s : %s %s", i2.toString(), i2.__vptr, i2.__monitor);
 
 The output:
 
 Same Value.
 Same Object.
 3 : 806D3F4 0
 3 : 806D3F4 0
 5 : 806D3F4 0
 5 : 806D3F4 0
 
 Ali

I probably wasn't clear about what exactly my problem is : Somehow in my (D1) program two object references have different vpointers/monitors and thus fail in "is" equality but I can change both objects with one call.
Apr 04 2010
parent reply Justin Spahr-Summers <Justin.SpahrSummers gmail.com> writes:
On Sun, 04 Apr 2010 13:14:29 -0400, strtr <strtr spam.com> wrote:
 
 Ali Çehreli Wrote:
 
 The code works as expected with 2.042
 
 I had to modify the toString() functions to return string, and say 
 "override" in C's toString definition; and had to modify the writefln() 
 calls:
 
    writefln("%s : %s %s", i2.toString(), i2.__vptr, i2.__monitor);
 
 The output:
 
 Same Value.
 Same Object.
 3 : 806D3F4 0
 3 : 806D3F4 0
 5 : 806D3F4 0
 5 : 806D3F4 0
 
 Ali

I probably wasn't clear about what exactly my problem is : Somehow in my (D1) program two object references have different vpointers/monitors and thus fail in "is" equality but I can change both objects with one call.

Again, without seeing the *actual* code that you are using, it's hard to say. The first thing that comes to mind is maybe a syntactical mistake, such as "i1.value = i2.value" when a comparison was intended, but there could be something else at play too, and without seeing the code (which is not the same as what you included in your first message) it's impossible to ascertain.
Apr 04 2010
parent reply strtr <strtr spam.com> writes:
== Quote from Justin Spahr-Summers (Justin.SpahrSummers gmail.com)'s article

 Again, without seeing the *actual* code that you are using, it's hard to
 say. The first thing that comes to mind is maybe a syntactical mistake,
 such as "i1.value = i2.value" when a comparison was intended, but there
 could be something else at play too, and without seeing the code (which
 is not the same as what you included in your first message) it's
 impossible to ascertain.

But also, the question remains :) (I haven't been able to replicate it in small program but the main is actual code.) Is it possible to have different vpointers/monitors pointing to the same object?
Apr 04 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
strtr:
 Is it possible to have different vpointers/monitors pointing to the same
object?

I think this questions is meaningless. Those pointers don't point to objects. And the "is" operator compared class references, not vpointers/monitors. Bye, bearophile
Apr 04 2010
parent reply strtr <strtr spam.com> writes:
bearophile Wrote:

 strtr:
 Is it possible to have different vpointers/monitors pointing to the same
object?

I think this questions is meaningless. Those pointers don't point to objects. And the "is" operator compared class references, not vpointers/monitors. Bye, bearophile

The subject wasn't for nothing :) How I understand it now, vptr points to the Class's vtable. This would mean they should be the same for references to the same object or can these references also hold a pointer to a pointer to the vtable? I'm not sure where the monitor points to, though. The problem surfaces when I wanted to do a simple check for objects equality between references. I though I should use "is" for this. But to my astonishment it failed even though I know the references point to the same object, I think. I checked whether the references pointed to the same object by giving all objects a unique value and changing this value via one of the references and then checking whether the value was the same for the other reference. Conclusion: two references fail "is", but changing the value via one of the references also changed the value got via the other reference. My solution for now is simply checking for equality via this unique value but I hoped I could use "is" for this :) I suspect I totally misunderstand something; hope to learn something again :)
Apr 06 2010
parent reply bearophile <bearophileHUGS lycos.com> writes:
Time ago I have told Walter that adding images with pointers and boxes to the D
docs, that represent the main data structures used in D, can help a lot the
understanding and usage of D.

When you *see* the data structure in an image, understanding what happens and
how to write program gets easy. So I'd like to see images of the virtual table,
an object, interface, etc.

A class reference is a pointer, that is the index (an integer number) of the
first byte of RAM of a segment of RAM.

The segment of ram contains the class instance data, plus two more pointers at
its start, one to the virtual table, and one to the monitor. The virtual table
is a struct that contains indirect pointers to the virtual functions and more.
I don't know the layout of the virtual tables in dmd. And I have no idea how
the monitor is structured, it's a mystery for me still. I am learning still.

All the instances of a single class point to the same virtual table. Instances
of different classes contain pointers to different virtual tables. All objects
in D have a pointer to VPT, even classes with no virtual methods. (Structs have
no pointer to monitor and vtbl, but they can have a pointer to outer scope if
they are not static. This is true for classes too, and there is a little more
complexity coming from template instantiations inside functions in D).

When you instantiate a class you create a new section of memory that contains
the class members, plus the two pointers.

You can have more than one reference to the same object. And two distinct
objects in memory can have the same data into their members.

The "is" operator just compares the class instances, if they are equal (the
optimizer can avoid some tests if it knows the objects are surely different,
because it knows the static types).

The == among class instances does several things. See D docs page about
operator overloading. But basically it tests if the references are the same. If
it's true, then they are two references to the same class instance, so they
must be equal, and the == returns true.
If the references are different it tests for equality of all members, and
returns true if they are all equal, otherwise false.

Bye,
bearophile
Apr 06 2010
next sibling parent reply Justin Spahr-Summers <Justin.SpahrSummers gmail.com> writes:
On Tue, 06 Apr 2010 21:15:02 -0400, bearophile 
<bearophileHUGS lycos.com> wrote:
 
 Time ago I have told Walter that adding images with pointers and boxes to the
D docs, that represent the main data structures used in D, can help a lot the
understanding and usage of D.
 
 When you *see* the data structure in an image, understanding what happens and
how to write program gets easy. So I'd like to see images of the virtual table,
an object, interface, etc.
 
 A class reference is a pointer, that is the index (an integer number) of the
first byte of RAM of a segment of RAM.
 
 The segment of ram contains the class instance data, plus two more pointers at
its start, one to the virtual table, and one to the monitor. The virtual table
is a struct that contains indirect pointers to the virtual functions and more.
I don't know the layout of the virtual tables in dmd. And I have no idea how
the monitor is structured, it's a mystery for me still. I am learning still.
 
 All the instances of a single class point to the same virtual table. Instances
of different classes contain pointers to different virtual tables. All objects
in D have a pointer to VPT, even classes with no virtual methods. (Structs have
no pointer to monitor and vtbl, but they can have a pointer to outer scope if
they are not static. This is true for classes too, and there is a little more
complexity coming from template instantiations inside functions in D).
 
 When you instantiate a class you create a new section of memory that contains
the class members, plus the two pointers.
 
 You can have more than one reference to the same object. And two distinct
objects in memory can have the same data into their members.
 
 The "is" operator just compares the class instances, if they are equal (the
optimizer can avoid some tests if it knows the objects are surely different,
because it knows the static types).
 
 The == among class instances does several things. See D docs page about
operator overloading. But basically it tests if the references are the same. If
it's true, then they are two references to the same class instance, so they
must be equal, and the == returns true.
 If the references are different it tests for equality of all members, and
returns true if they are all equal, otherwise false.
 
 Bye,
 bearophile

I think he said that he has two distinct object references, but the value stored in the object(s) changes by changing either one. In other words, we'd need to see the code.
Apr 06 2010
parent reply strtr <strtr spam.com> writes:
Justin Spahr-Summers Wrote:
 
 I think he said that he has two distinct object references, but the 
 value stored in the object(s) changes by changing either one.
 
 In other words, we'd need to see the code.

I've added this exact sequence: if( c1 !is null ) { c1.value = 1; if( c2 !is null ) { c2.value = 2; if( c1 !is c2 ) { c1.value = 3; assert(c2.value == 2 ); } c2.value = 0; } c1.value = 0; } To my understanding this should never fails, yet it does. AssertError Failure
Apr 06 2010
parent reply Justin Spahr-Summers <Justin.SpahrSummers gmail.com> writes:
On Tue, 06 Apr 2010 22:41:43 -0400, strtr <strtr spam.com> wrote:
 
 Justin Spahr-Summers Wrote:
 
 I think he said that he has two distinct object references, but the 
 value stored in the object(s) changes by changing either one.
 
 In other words, we'd need to see the code.

I've added this exact sequence: if( c1 !is null ) { c1.value = 1; if( c2 !is null ) { c2.value = 2; if( c1 !is c2 ) { c1.value = 3; assert(c2.value == 2 ); } c2.value = 0; } c1.value = 0; } To my understanding this should never fails, yet it does. AssertError Failure

Hmm, that is pretty weird. Are you doing any casts anywhere, or any pointer arithmetic/tricks? The only thing that I can think of is that you might've somehow unintentionally fooled the compiler/runtime by coercing some types somewhere. If not, it might comprise a valid bug report.
Apr 06 2010
parent reply strtr <strtr spam.com> writes:
Justin Spahr-Summers Wrote:
 
 Hmm, that is pretty weird. Are you doing any casts anywhere, or any 
 pointer arithmetic/tricks?

Do you maybe know another thing to check? I do throw references around and there are a lot of implicit casts to extended interfaces.
 
 The only thing that I can think of is that you might've somehow 
 unintentionally fooled the compiler/runtime by coercing some types 
 somewhere.

 
 If not, it might comprise a valid bug report.

Apr 06 2010
parent Justin Spahr-Summers <Justin.SpahrSummers gmail.com> writes:
On Tue, 06 Apr 2010 23:35:01 -0400, strtr <strtr spam.com> wrote:
 
 Justin Spahr-Summers Wrote:
 
 Hmm, that is pretty weird. Are you doing any casts anywhere, or any 
 pointer arithmetic/tricks?

Do you maybe know another thing to check? I do throw references around and there are a lot of implicit casts to extended interfaces.

Without actual explicit casting, I don't know how it'd be possible to invoke behavior like that. Maybe an object of class X getting implicitly converted to interface A and then explicitly cast to class Y? It'd have to be pretty convoluted.
Apr 07 2010
prev sibling parent strtr <strtr spam.com> writes:
bearophile Wrote:

 Time ago I have told Walter that adding images with pointers and boxes to the
D docs, that represent the main data structures used in D, can help a lot the
understanding and usage of D.
 
 When you *see* the data structure in an image, understanding what happens and
how to write program gets easy. So I'd like to see images of the virtual table,
an object, interface, etc.
 
 A class reference is a pointer, that is the index (an integer number) of the
first byte of RAM of a segment of RAM.
 
 The segment of ram contains the class instance data, plus two more pointers at
its start, one to the virtual table, and one to the monitor. The virtual table
is a struct that contains indirect pointers to the virtual functions and more.
I don't know the layout of the virtual tables in dmd. And I have no idea how
the monitor is structured, it's a mystery for me still. I am learning still.
 
 All the instances of a single class point to the same virtual table. Instances
of different classes contain pointers to different virtual tables. All objects
in D have a pointer to VPT, even classes with no virtual methods. (Structs have
no pointer to monitor and vtbl, but they can have a pointer to outer scope if
they are not static. This is true for classes too, and there is a little more
complexity coming from template instantiations inside functions in D).
 
 When you instantiate a class you create a new section of memory that contains
the class members, plus the two pointers.
 
 You can have more than one reference to the same object. And two distinct
objects in memory can have the same data into their members.
 
 The "is" operator just compares the class instances, if they are equal (the
optimizer can avoid some tests if it knows the objects are surely different,
because it knows the static types).
 
 The == among class instances does several things. See D docs page about
operator overloading. But basically it tests if the references are the same. If
it's true, then they are two references to the same class instance, so they
must be equal, and the == returns true.
 If the references are different it tests for equality of all members, and
returns true if they are all equal, otherwise false.
 
 Bye,
 bearophile

I totally agree on the visual help! I think I'll create such a diagram after I get my brain mess cleaned up :) Your explanation doesn't differ (but does expand ;) from my understanding of the whole class implementation subject. I still do not know what a monitor is though ;)
Apr 06 2010