www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 1778] New: Can not create pointer to class reference

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778

           Summary: Can not create pointer to class reference
           Product: D
           Version: 1.026
          Platform: PC
        OS/Version: All
            Status: NEW
          Severity: major
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla digitalmars.com
        ReportedBy: aarti interia.pl


Below doesn't compile:
----
A* cp = new A*;
----
Type of new A* is (A**) while it should be A*.

Reference:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=10683


-- 
Jan 10 2008
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778


bugzilla digitalmars.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |INVALID





In general,
   new T;
will return a value of type T*, unless T is a reference type (i.e. a class or
an array), in which case it returns a value of type T.
Therefore,
   new T*;
will allocate a pointer to T, and return a value which points to that pointer
to T. In other words, a T**.

This is as designed.


-- 
Jan 16 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778


aarti interia.pl changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|RESOLVED                    |REOPENED
         Resolution|INVALID                     |





Well, the problem is not that simple as it seems to be...

Currently it is NOT possible to create in sane way pointer to class reference
on heap. It affects also all types of form:
A**, A***, A**** ...., where A is a class
because to initialize them it is necessary at last to initialize A*.

Because of pointer is not created, there is no way to initialize such a
variable.

More comments in below example:
import std.stdio;

T create(T)() {
    static if (is(T == class))
        return new T;
    else
        return *(new T);
}

class A { void test() {writefln("Ok!");}}
struct B {}

void main() {
    //Does not create pointer to A in memory
    A* x = *(new A*);
    writefln("x is null: ", x is null);
    //*x = new A; //access violation, because x is null as only 
                  //first pointer is created

    //How to initialize below variable?
    A** y = new A*;
    //*y = ????


    //Workaroud 1
    A* u = cast(A*) new B;
    *u = new A;
    u.test;

    //Workaround 2
    static A ca;
    ca = new A;
    u = &ca;
    //unfortunately in this case there remains memory leak

    //Workaroud 3
    //IMHO best long term solution, but it is difficult for me to predict all
    //consequences
    //Allow that typeof(A*) == typeof(A)
    //and typeof(&A) == typeof(A)
    //for classes. It will allow also to create one create function for 
    //all types:
    //T* create(T)() {
    //  return new T;
    //}

    //Workaround 4
    //When creating (new A*)
    //initialize not only first pointer
    //but also second, so that
    // *(new A*) is not null
}


-- 
Jan 17 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778


bugzilla digitalmars.com changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|REOPENED                    |RESOLVED
         Resolution|                            |INVALID





C c = new C;
C* pc = new C*;
*pc = &c;

Note that it appears you are asking that:
   new C*;
allocate both a C and a pointer to C as two separate allocations. This is not
done for the same reason that:
   new int*;
does only one allocation - not two.


-- 
Jan 17 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778






Well, your solution is IMHO wrong for most real-life use cases. (And that was
my first try to solve problem).

BTW your example should look like below (there is an error in your's):
C c = new C;
C** ppc = new C*;
*ppc = &c;

And now, let's put this code into function which returns pointer to C:
---
import std.stdio;
class C {void test() {writefln("Ok");}}

C* func() {
    C c = new C;
    C** ppc = new C*;
    *ppc = &c;
    return *ppc;
}

void main() {func.test;}

---
Something that I don't understand in above code is the fact that it... works!
But it should definitely not!

Please notice in function func that there is address of local variable taken.
After exiting scope this address might be very quickly invalid. Without all
this pointer machinery, when you put just return &c; at the end of function you
get from compiler:

src/quicktest.d(7): Error: escaping reference to local variable c

IMHO it works just because of some specific stack access optimization in
my/others? computer. Or compiler makes some magic? 

I don't know why it works, but IMHO this bug should be reopened...


-- 
Jan 17 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778






1) Why is it wrong for real live cases?
2) You're right, pc should be declared C**
3) The return "worked" because the value still existed, undamaged, on the
stack. The stack cleanup just adjusts the ESP, it doesn't actually stomp on the
values


-- 
Jan 17 2008
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778






You can also write:

class C { }
void test()
{
 C c = new C;
 C* pc = (new C[1]).ptr;
 *pc = c;
}


-- 
Jan 17 2008
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=1778






 1) Why is it wrong for real live cases?
Because probably in most cases, when you will need something like pointer to class you will need also to return it from function. And then you have a problem with escaping reference to local variable.
 3) The return "worked" because the value still existed, undamaged, on the
 stack. The stack cleanup just adjusts the ESP, it doesn't actually stomp on the
 values
Well it confirms that it was just plain luck, that program was working... 4) (answer to your next comment) class C { } void test() { C c = new C; C* pc = (new C[1]).ptr; *pc = c; } It seems to be similar workaround like number 1 from my workaround list? And as I assume it should work when returning from function? ------------- It's nice that there are workarounds which are actually working right now. But IMHO they are still only workarounds (quick hacks). And according to principle "No issue left behind", it should be rather solved than hacked. Maybe you would consider my proposition: *** For A being a class, types A and A* should be implicitly castable between each other. *** Known consequences (probably just iceberg top, but let me start): 1. In index of associative array: if type of index is A* array should be indexed by pointer, if type of index is A, array should be indexed by opHash 2. Proper reductions needs to be applied for types like A**, A*** etc. (see below) ... Advantages: It will reduce number of necessary workarounds in code: 1. In constructor-like functions: instead of currently necessary: T create(T)() { static if (is(T == class)) return new T; else return *(new T); } 2. When storing pointers to class in associative arrays. Currently I have to cast A to (void*) to store into associative array. With new solution I would just declare array index as A* and put class there. 3. It solves cleanly problem of creation pointer to class A** p = new A*; which, after reduction is same as: (A*)* p = new (A*); and A* p = new A; 4. This change is not sophisticated workaround to hide real state of matter, but rather exposes real state of matter which is that new A; creates pointer to class A. Anyway I would say that this bug should stay open, till sane solution will be found. --
Jan 18 2008