www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - comparing pointers passed to and returned from funcs

reply spir <denis.spir gmail.com> writes:
Hello,

It seems to be the kind of stupid issue that will make you laugh about me. But 
I cannot grasp and want to move forward anyway; so, let us be bold and take the 
risk ;-)

I'm modeling a little dynamic language. Elements (values, objects) are pointers 
to structs (actually tagged unions) allocated on the heap. I have a problem in 
passing and returning those pointers to and from primitive procedures.

Precisely, unlike in D, Logical (boolean) operations only accept Logical 
elements true/false (called TRUE/FALSE on implementation side for obvious
reason):
     enum TRUE = new DElement(true);
     enum FALSE = new DElement(false);

  So, I thought I could write those operations by comparing pointers directly,
eg:
     Element not (Element operand) {
         // checks type
         operand.checkLogical()
         return (operand == TRUE) ? FALSE : TRUE;
     }
     ...
     assert ( not(TRUE) == FALSE );

This fails! It even fails "doubly"...
When I call not(TRUE), TRUE (the pointer) inside the func is not equal to the 
global constant. Thus, not always returns FALSE. And in fact this is also 
wrong, because I have the same problem on the return value as well: the FALSE 
returned is not equal to the global FALSE.

But the pointed structs are ok. Each one holds a D boolean of the correct value 
(a member called 'logical'). Thus, the following succeeds:

     Element not (Element operand) {
         // checks type & returns the 'logical' member
         auto log = operand.checkLogical();
         return (log) ? FALSE : TRUE;
     }
     ...
     assert ( not(TRUE).logical == false );

Here, I operate on the structs instead of on the pointers, both to perform the 
operation and in the assert. What I understand is: all happens like if D would 
copy the pointed structs on parameter passing and on return. I thought D would 
only copy the pointers (in both directions), which would let me compare said 
pointers directly.
What do I miss?

It is not a serious problem since the workaround is easy and not very costly. 
But I wish to understand why I cannot operate on constant 'identity'. As said 
above, this must a trivial issue I simply cannot guess...

Thank you,
Denis
-- 
_________________
vita es estrany
spir.wikidot.com
Mar 01 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
spir:

 It seems to be the kind of stupid issue that will make you laugh about me. But 
 I cannot grasp and want to move forward anyway; so, let us be bold and take
the 
 risk ;-)
Be bold. Understanding things is much more important. I've being wrong hundreds of times on D newsgroups, but I'm getting better.
 This fails! It even fails "doubly"...
Reduced program (and Walter thinks that avoiding printf-related bugs is not important. He's wrong. I use printf often enough in D): import core.stdc.stdio: printf; struct Foo { bool b; this(bool b_) { this.b = b_; } } enum Foo* TRUE = new Foo(true); enum Foo* FALSE = new Foo(false); Foo* not(Foo* op) { return (op == TRUE) ? FALSE : TRUE; } void main() { //assert(not(TRUE) == FALSE); printf("%x\n", TRUE); printf("%x\n", FALSE); printf("%x\n", not(TRUE)); printf("%x\n", not(not(TRUE))); }
 Here, I operate on the structs instead of on the pointers, both to perform the 
 operation and in the assert. What I understand is: all happens like if D would 
 copy the pointed structs on parameter passing and on return. I thought D would 
 only copy the pointers (in both directions), which would let me compare said 
 pointers directly.
 What do I miss?
DMD is not copying the structs, just pointers. I think it's happening something like with enum associative arrays. Pointers are run-time things. You are asking for a new at compile-time. This is the asm produced by that program (-O -release): _D4test3Foo6__ctorMFNcbZS4test3Foo comdat push EAX mov CL,8[ESP] mov [EAX],CL pop ECX ret 4 _D4test3notFPS4test3FooZPS4test3Foo comdat L0: push EAX mov ECX,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push EAX push EBX push 1 push ECX call near ptr __d_newarrayT add ESP,8 mov EAX,EDX push 1 call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo cmp EAX,8[ESP] jne L3D mov EDX,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push 1 push EDX call near ptr __d_newarrayT add ESP,8 mov EAX,EDX push 0 call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo jmp short L56 L3D: mov EBX,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push 1 push EBX call near ptr __d_newarrayT add ESP,8 mov EAX,EDX push 1 call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo L56: pop EBX add ESP,8 ret __Dmain comdat L0: push EAX mov EAX,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push EBX push ESI push 1 push 1 push EAX call near ptr __d_newarrayT add ESP,8 mov EAX,EDX call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo mov ECX,offset FLAT:_DATA push EAX push ECX call near ptr _printf mov EDX,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push 0 push 1 push EDX call near ptr __d_newarrayT add ESP,8 mov EAX,EDX call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo mov EBX,offset FLAT:_DATA push EAX push EBX call near ptr _printf mov ESI,offset FLAT:_D20TypeInfo_PS4test3Foo6__initZ push 1 push 1 push ESI call near ptr __d_newarrayT add ESP,8 mov EAX,EDX call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo call near ptr _D4test3notFPS4test3FooZPS4test3Foo push EAX push EBX call near ptr _printf push 1 push 1 push ESI call near ptr __d_newarrayT add ESP,8 mov EAX,EDX call near ptr _D4test3Foo6__ctorMFNcbZS4test3Foo call near ptr _D4test3notFPS4test3FooZPS4test3Foo call near ptr _D4test3notFPS4test3FooZPS4test3Foo push EAX push EBX call near ptr _printf add ESP,020h xor EAX,EAX pop ESI pop EBX pop ECX ret My suspect is confirmed, DMD is acting as with enum associative arrays. This is a compiler bug. DMD has to act correctly and not create a struct every time, or it has to give a compile-time error. Not this mess. Bye, bearophile
Mar 01 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
This seems to work:

import core.stdc.stdio: printf;
struct Foo {
    bool b;
    this(bool b_) { this.b = b_; }
}
const(Foo)* TRUE, FALSE;
static this() {
    TRUE = new const(Foo)(true);
    FALSE = new const(Foo)(false);
}
const(Foo)* not(const(Foo)* op) {
    return (op == TRUE) ? FALSE : TRUE;
}
void main() {
    assert(not(TRUE) == FALSE);
    printf("%x\n", TRUE);
    printf("%x\n", FALSE);
    printf("%x\n", not(TRUE));
    printf("%x\n", not(not(TRUE)));
}

Bye,
bearophile
Mar 01 2011
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
http://d.puremagic.com/issues/show_bug.cgi?id=5678
Mar 01 2011
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 01 Mar 2011 18:11:00 -0500, bearophile <bearophileHUGS lycos.com>  
wrote:

 http://d.puremagic.com/issues/show_bug.cgi?id=5678
I think there is a general bug where any time the compiler uses an enum, it simply replaces the expression declared for the enum. So basically enum TRUE = new DElement(true); void main() { auto delem1 = TRUE; auto delem2 = TRUE; assert(delem1 is delem2); // fails } gets morphed into this: void main() { auto delem1 = new Delement(true); auto delem2 = new Delement(true); assert(delem1 is delem2); // fails } Obviously this works great when the enum is a value type or a string literal (which is created at compile time). However, it is not so great for things like AAs, array literals, objects, or structs. I think there are a few of these bugs in bugzilla, and there should be at least a tracker, and if not, they should all be combined. This is a serious problem in D, and really creates havoc (both performance-wise and semantically). I don't anticipate there is an easy fix. Essentially, I'd say enum is completely useless except for builtin types and strings. -Steve
Mar 02 2011
parent spir <denis.spir gmail.com> writes:
On 03/02/2011 02:24 PM, Steven Schveighoffer wrote:
 On Tue, 01 Mar 2011 18:11:00 -0500, bearophile <bearophileHUGS lycos.com>
wrote:

 http://d.puremagic.com/issues/show_bug.cgi?id=5678
I think there is a general bug where any time the compiler uses an enum, it simply replaces the expression declared for the enum. So basically enum TRUE = new DElement(true); void main() { auto delem1 = TRUE; auto delem2 = TRUE; assert(delem1 is delem2); // fails } gets morphed into this: void main() { auto delem1 = new Delement(true); auto delem2 = new Delement(true); assert(delem1 is delem2); // fails } Obviously this works great when the enum is a value type or a string literal (which is created at compile time). However, it is not so great for things like AAs, array literals, objects, or structs. I think there are a few of these bugs in bugzilla, and there should be at least a tracker, and if not, they should all be combined. This is a serious problem in D, and really creates havoc (both performance-wise and semantically). I don't anticipate there is an easy fix. Essentially, I'd say enum is completely useless except for builtin types and strings.
Thank you Steven & Bearophile. This solves my problem. I first did not get Bearophile's answer about run/compile-time constants (I mean enums). I thought the time when they are created is irrelevant, isn't it? Anyway, placing the constant defs inside a module's <static this () {...}> block does what I mean. It does, in fact, ensure *unicity*. But I don't really understand the relation with Steven's explanation above: why/how does the fact that a constant's def is inside static this() prevent the compiler to rewrite it like shown above? Also, I basically don't understand why dmd does that anyway: it's obviously un-ecological ;-) Denis -- _________________ vita es estrany spir.wikidot.com
Mar 02 2011
prev sibling parent reply Bekenn <leaveme alone.com> writes:
On 3/1/11 3:00 PM, bearophile wrote:
 const(Foo)* TRUE, FALSE;
I'd remove those parens; you don't want people modifying TRUE or FALSE.
Mar 01 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Bekenn:

 I'd remove those parens; you don't want people modifying TRUE or FALSE.
Please, show me working code that implements your idea :-) Bye, bearophile
Mar 01 2011
next sibling parent reply Bekenn <leaveme alone.com> writes:
On 3/1/11 4:12 PM, bearophile wrote:
 Bekenn:

 I'd remove those parens; you don't want people modifying TRUE or FALSE.
Please, show me working code that implements your idea :-)
Touche. I'll have to test that out once I get back from work...
Mar 01 2011
parent reply bearophile <bearophileHUGS lycos.com> writes:
Bekenn:

 Touche.  I'll have to test that out once I get back from work...
Sorry for that answer of mine, we are here to learn and cooperate, not to fight :-) It's just I sometimes have problems with const things in D, and sometimes DMD has problems with const things. Bye, bearophile
Mar 01 2011
parent Bekenn <leaveme alone.com> writes:
On 3/1/11 5:31 PM, bearophile wrote:
 Bekenn:

 Touche.  I'll have to test that out once I get back from work...
Sorry for that answer of mine, we are here to learn and cooperate, not to fight :-) It's just I sometimes have problems with const things in D, and sometimes DMD has problems with const things.
Hey, no problem; I thought it was funny...
Mar 01 2011
prev sibling parent Bekenn <leaveme alone.com> writes:
On 3/1/2011 4:12 PM, bearophile wrote:
 Bekenn:

 I'd remove those parens; you don't want people modifying TRUE or FALSE.
Please, show me working code that implements your idea :-) Bye, bearophile
Here you go; I only changed the one line. Compiles and works just fine in dmd 2.051. const Foo* TRUE, FALSE; instead of const(Foo)* TRUE, FALSE;
Mar 01 2011