www.digitalmars.com         C & C++   DMDScript  

D - Bug or feature?

reply Helmut Leitner <helmut.leitner chello.at> writes:
In a situation
  class B:A { ... }
I can call
  foo(A a);
passing instances of class A or class B.

Now I would like to write a function 
  foo(A [] atab)
that I can pass arrays of instances of A or B. 
But I can't make it happen. It won't compile.

It's the same when I use interfaces.

My sample code (contains both trys):

    interface Printable {
      void print();
    }

    class A:Printable {
      void print() { printf("A\n"); }
    }

    class B:A {
      void print() { printf("B\n"); }
    }

    void Print(A a) {
      a.print();
    }

    void ArrayPrint(A a[]) {
      for(int i=0; i<a.length; i++) {
        printf("[%d]: ",i); a[i].print();
      }
    }

    void PrintablePrint(Printable p) {
      printf("via Interface: "); p.print();
    }

    void ArrayPrintablePrint(Printable [] p) {
      for(int i=0; i<p.length; i++) {
        printf("via Interface [%d]: ",i); PrintablePrint(p[i]);
      }
    }

    int main ()
    {
      A atab[];

      atab.length=3;
      atab[0]= new A;
      atab[1]= new A;
      atab[2]= new A;

      Print(atab[0]);
      ArrayPrint(atab);

      B btab[];
      btab.length=3;
      btab[0]= new B;
      btab[1]= new B;
      btab[2]= new B;

      Print(btab[0]);
      // ArrayPrint(btab); //  doesn't compile

      PrintablePrint(atab[0]);
      PrintablePrint(btab[0]);

      // ArrayPrintablePrint(atab); // doesn't compile
      // ArrayPrintablePrint(btab); // doesn't compile

      return 0;
    }

Shouldn't the 3 commented lines work?

-- 
Helmut Leitner    leitner hls.via.at
Graz, Austria   www.hls-software.com
Aug 31 2003
next sibling parent reply "Jeroen van Bemmel" <someone somewhere.com> writes:
When you declare an array of class A[N], the compiler will allocate
N*sizeof(A) + a bit of memory for it. You cannot then store an element B
(derived from A) in that same array, since sizeof(B) is typically larger
than sizeof(A) which would confuse indexing calculations.

So basically, the case here is that A[] is a totally different type than
B[], even though B may be derived from A. 'Type substitutionability'
(somebody rephrase this :)as a result of inheritance does not apply to
arrays

You would be able to create an array of _pointers_ to A, which could then
point to instances of B as you like.

The answer to your question should thus be: feature

"Helmut Leitner" <helmut.leitner chello.at> wrote in message
news:3F524DD2.84943F44 chello.at...
 In a situation
   class B:A { ... }
 I can call
   foo(A a);
 passing instances of class A or class B.

 Now I would like to write a function
   foo(A [] atab)
 that I can pass arrays of instances of A or B.
 But I can't make it happen. It won't compile.

 It's the same when I use interfaces.

 My sample code (contains both trys):

     interface Printable {
       void print();
     }

     class A:Printable {
       void print() { printf("A\n"); }
     }

     class B:A {
       void print() { printf("B\n"); }
     }

     void Print(A a) {
       a.print();
     }

     void ArrayPrint(A a[]) {
       for(int i=0; i<a.length; i++) {
         printf("[%d]: ",i); a[i].print();
       }
     }

     void PrintablePrint(Printable p) {
       printf("via Interface: "); p.print();
     }

     void ArrayPrintablePrint(Printable [] p) {
       for(int i=0; i<p.length; i++) {
         printf("via Interface [%d]: ",i); PrintablePrint(p[i]);
       }
     }

     int main ()
     {
       A atab[];

       atab.length=3;
       atab[0]= new A;
       atab[1]= new A;
       atab[2]= new A;

       Print(atab[0]);
       ArrayPrint(atab);

       B btab[];
       btab.length=3;
       btab[0]= new B;
       btab[1]= new B;
       btab[2]= new B;

       Print(btab[0]);
       // ArrayPrint(btab); //  doesn't compile

       PrintablePrint(atab[0]);
       PrintablePrint(btab[0]);

       // ArrayPrintablePrint(atab); // doesn't compile
       // ArrayPrintablePrint(btab); // doesn't compile

       return 0;
     }

 Shouldn't the 3 commented lines work?

 -- 
 Helmut Leitner    leitner hls.via.at
 Graz, Austria   www.hls-software.com

Aug 31 2003
next sibling parent "Vathix" <vathix dprogramming.com> writes:
But D uses class references, which isn't the actual memory but a disguised
pointer (?) so I think it should work. It would be a problem for structs,
but they aren't allowed to have inheritance.

"Jeroen van Bemmel" <someone somewhere.com> wrote in message
news:bitnrj$204b$1 digitaldaemon.com...
 When you declare an array of class A[N], the compiler will allocate
 N*sizeof(A) + a bit of memory for it. You cannot then store an element B
 (derived from A) in that same array, since sizeof(B) is typically larger
 than sizeof(A) which would confuse indexing calculations.

 So basically, the case here is that A[] is a totally different type than
 B[], even though B may be derived from A. 'Type substitutionability'
 (somebody rephrase this :)as a result of inheritance does not apply to
 arrays

 You would be able to create an array of _pointers_ to A, which could then
 point to instances of B as you like.

 The answer to your question should thus be: feature

 "Helmut Leitner" <helmut.leitner chello.at> wrote in message
 news:3F524DD2.84943F44 chello.at...
 In a situation
   class B:A { ... }
 I can call
   foo(A a);
 passing instances of class A or class B.

 Now I would like to write a function
   foo(A [] atab)
 that I can pass arrays of instances of A or B.
 But I can't make it happen. It won't compile.

 It's the same when I use interfaces.

 My sample code (contains both trys):

     interface Printable {
       void print();
     }

     class A:Printable {
       void print() { printf("A\n"); }
     }

     class B:A {
       void print() { printf("B\n"); }
     }

     void Print(A a) {
       a.print();
     }

     void ArrayPrint(A a[]) {
       for(int i=0; i<a.length; i++) {
         printf("[%d]: ",i); a[i].print();
       }
     }

     void PrintablePrint(Printable p) {
       printf("via Interface: "); p.print();
     }

     void ArrayPrintablePrint(Printable [] p) {
       for(int i=0; i<p.length; i++) {
         printf("via Interface [%d]: ",i); PrintablePrint(p[i]);
       }
     }

     int main ()
     {
       A atab[];

       atab.length=3;
       atab[0]= new A;
       atab[1]= new A;
       atab[2]= new A;

       Print(atab[0]);
       ArrayPrint(atab);

       B btab[];
       btab.length=3;
       btab[0]= new B;
       btab[1]= new B;
       btab[2]= new B;

       Print(btab[0]);
       // ArrayPrint(btab); //  doesn't compile

       PrintablePrint(atab[0]);
       PrintablePrint(btab[0]);

       // ArrayPrintablePrint(atab); // doesn't compile
       // ArrayPrintablePrint(btab); // doesn't compile

       return 0;
     }

 Shouldn't the 3 commented lines work?

 --
 Helmut Leitner    leitner hls.via.at
 Graz, Austria   www.hls-software.com


Aug 31 2003
prev sibling next sibling parent "Riccardo De Agostini" <riccardo.de.agostini email.it> writes:
"Jeroen van Bemmel" <someone somewhere.com> ha scritto nel messaggio
news:bitnrj$204b$1 digitaldaemon.com...
 [...] 'Type substitutionability' (somebody rephrase this :) [...]

Polymorphism, maybe? Ric
Sep 01 2003
prev sibling parent reply Helmut Leitner <helmut.leitner chello.at> writes:
Jeroen van Bemmel wrote:
 
 When you declare an array of class A[N], the compiler will allocate
 N*sizeof(A) + a bit of memory for it. 

I don't think so. I think there is an array reference consisting of - array size - pointer to start of array and the array of object reference - N * 4 byte of heap storage and perhaps (if initialized) - N * sizeof(A) of heap storage for the instances But I'm can't be quite sure about these implementation detail.
 You cannot then store an element B
 (derived from A) in that same array, since sizeof(B) is typically larger
 than sizeof(A) which would confuse indexing calculations.
 
 So basically, the case here is that A[] is a totally different type than
 B[], even though B may be derived from A. 'Type substitutionability'
 (somebody rephrase this :)as a result of inheritance does not apply to
 arrays

If I cast: ArrayPrint( cast(A []) btab ); it compiles and works, but this type of casting seems very unsafe to me (if I cast to the interface Printable it also compiles but <crashes>). -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
Sep 01 2003
parent "Jeroen van Bemmel" <someone somewhere.com> writes:
 But I'm can't be quite sure about these implementation detail.

I'm not sure either, my posting was based on C++ semantics. Did you try the pointer-to-A array ?
 You cannot then store an element B
 (derived from A) in that same array, since sizeof(B) is typically larger
 than sizeof(A) which would confuse indexing calculations.

 So basically, the case here is that A[] is a totally different type than
 B[], even though B may be derived from A. 'Type substitutionability'
 (somebody rephrase this :)as a result of inheritance does not apply to
 arrays

If I cast: ArrayPrint( cast(A []) btab ); it compiles and works, but this type of casting seems very unsafe to me (if I cast to the interface Printable it also compiles but <crashes>).

Try adding a datamember to B and see if the casting still works.
 -- 
 Helmut Leitner    leitner hls.via.at
 Graz, Austria   www.hls-software.com

Sep 01 2003
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
It's a bug. Since A[] is an array of references to A, B[] should be
implicitly convertible to A[]. Thanks for finding it, this is an important
one. -Walter
Sep 01 2003
next sibling parent reply "Jeroen van Bemmel" <someone somewhere.com> writes:
Walter,

Could you add this information to the 'Arrays' section of the D webpages?
It's counter intuitive (at least for C++ people like me) and it makes me
give wrong answers :)

"Walter" <walter digitalmars.com> wrote in message
news:bj0e06$2nf0$1 digitaldaemon.com...
 It's a bug. Since A[] is an array of references to A, B[] should be
 implicitly convertible to A[]. Thanks for finding it, this is an important
 one. -Walter

Sep 01 2003
next sibling parent Helmut Leitner <leitner hls.via.at> writes:
Jeroen van Bemmel wrote:
 
 Walter,
 
 Could you add this information to the 'Arrays' section of the D webpages?
 It's counter intuitive (at least for C++ people like me) and it makes me
 give wrong answers :)
 

In addition there ar some new pages on Wiki4D <http://www.prowiki.org/wiki4d/wiki.cgi?NotesForProgrammersUsedTo> <http://www.prowiki.org/wiki4d/wiki.cgi?NotesForProgrammersUsedTo/CplusPlus> so that we can gather hints or special considerations for the users of various languages. -- Helmut Leitner leitner hls.via.at Graz, Austria www.hls-software.com
Sep 02 2003
prev sibling parent "Walter" <walter digitalmars.com> writes:
I think that's a good idea. I'll do it when I fix that bug. I remember when
I first was trying to learn Java I came to grief several times from
forgetting that Java does objects by reference.

"Jeroen van Bemmel" <someone somewhere.com> wrote in message
news:bj1adh$tkl$1 digitaldaemon.com...
 Walter,

 Could you add this information to the 'Arrays' section of the D webpages?
 It's counter intuitive (at least for C++ people like me) and it makes me
 give wrong answers :)

 "Walter" <walter digitalmars.com> wrote in message
 news:bj0e06$2nf0$1 digitaldaemon.com...
 It's a bug. Since A[] is an array of references to A, B[] should be
 implicitly convertible to A[]. Thanks for finding it, this is an


 one. -Walter


Sep 02 2003
prev sibling parent reply "DeadCow" <deadcow-remove-this free.fr> writes:
"Walter" <walter digitalmars.com> a écrit dans le message news:
bj0e06$2nf0$1 digitaldaemon.com...
 It's a bug. Since A[] is an array of references to A, B[] should be
 implicitly convertible to A[]. Thanks for finding it, this is an important
 one. -Walter

And what about it : class A {} class B : A { void crash() {}; } void doSomethingWrong( A[] as ) { as ~= new A(); // doh ! } void main() { B[] bs; doSomethingWrong(bs); // implicitly convertible you said bs[0].crash(); // oops... } -- Nicolas Repiquet
Sep 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
"DeadCow" <deadcow-remove-this free.fr> wrote in message
news:bj1vu2$1tke$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> a écrit dans le message news:
 bj0e06$2nf0$1 digitaldaemon.com...
 It's a bug. Since A[] is an array of references to A, B[] should be
 implicitly convertible to A[]. Thanks for finding it, this is an


 one. -Walter

And what about it : class A {} class B : A { void crash() {}; } void doSomethingWrong( A[] as ) { as ~= new A(); // doh ! } void main() { B[] bs; doSomethingWrong(bs); // implicitly convertible you said bs[0].crash(); // oops... }

In this case, 'as' is an in variable, and resizing it won't affect bs. If it was rewritten so that 'as' is inout, then it will crash as you showed. I don't know what to do about this case - perhaps disallow such implicit conversions for an inout or out parameter?
Sep 02 2003
parent reply "Walter" <walter digitalmars.com> writes:
I just realized there's another problem:

void doSomethingWrong(A[] as)
{
    as[0] = new A();
}

Now, main() will think that as[0] is a B, and crash away. This seems to be a
rather large hole in typesafety. The same problem exists in C++, so maybe
nobody cares anyway <g>.
Sep 02 2003
next sibling parent "Jeroen van Bemmel" <someone somewhere.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:bj2qee$1od$1 digitaldaemon.com...
 I just realized there's another problem:

 void doSomethingWrong(A[] as)
 {
     as[0] = new A();
 }

 Now, main() will think that as[0] is a B, and crash away. This seems to be

 rather large hole in typesafety. The same problem exists in C++, so maybe
 nobody cares anyway <g>.

Interesting test case. Java throws a 'java.lang.ArrayStoreException' here, at the point where you try to store the A in the array that is really B C++ is different, since arrays use store by value unless you make it an array of pointers. If I do, e.g. #include <iostream> using namespace std; class A { public: virtual void print() { cout << 'A' << endl; } }; class B : public A { public: virtual void print() { cout << 'B' << endl; } virtual void B_func() { cout << 'C' << endl; } }; static void test( A* as[] ) { as[0]->print(); as[0] = new A(); } int main( int argc, char* argv[] ) { B** bs = new (B*)[1]; bs[0] = new B(); test( (A**) bs ); // needs explicit cast bs[0]->B_func(); // crashes, probably because the vtable of A's class does not contain a pointer to B_func } So the need for an explicit cast should tell you that you're trying to do something illegal: B** is not implicitly translated to A** Although a rare case, I prefer the Java solution. The Java docs mention: Object x[] = new String[3]; x[0] = new Integer(0); as a typical case where such an exception would be thrown, and a more common one for that. Applicable to D too, Walter?
Sep 02 2003
prev sibling next sibling parent reply "Matthew Wilson" <matthew stlsoft.org> writes:
 I just realized there's another problem:

 void doSomethingWrong(A[] as)
 {
     as[0] = new A();
 }

 Now, main() will think that as[0] is a B, and crash away. This seems to be

 rather large hole in typesafety. The same problem exists in C++, so maybe
 nobody cares anyway <g>.

Large indeed! We need to think of some nice way to prevent this in general, but to allow the programmer to do it if s/he *really* wants to (kind of like poking COM vtable entries)
Sep 09 2003
parent Patrick Down <Patrick_member pathlink.com> writes:
In article <bjldla$2j4j$2 digitaldaemon.com>, Matthew Wilson says...
 I just realized there's another problem:

 void doSomethingWrong(A[] as)
 {
     as[0] = new A();
 }

 Now, main() will think that as[0] is a B, and crash away. This seems to be

 rather large hole in typesafety. The same problem exists in C++, so maybe
 nobody cares anyway <g>.

Large indeed! We need to think of some nice way to prevent this in general, but to allow the programmer to do it if s/he *really* wants to (kind of like poking COM vtable entries)

Store the class info as part of the array? assert(as.classinfo = A.classinfo);
Sep 09 2003
prev sibling parent "DeadCow" <deadcow-remove-this free.fr> writes:
"Walter" <walter digitalmars.com> a écrit dans le message news:
bj2qee$1od$1 digitaldaemon.com...

 Now, main() will think that as[0] is a B, and crash away. This seems to be

 rather large hole in typesafety. The same problem exists in C++, so maybe
 nobody cares anyway <g>.

What about templates ? class A {} class B {} template TFoo(T) { T[] ts; void add(T t) { ts ~= t; } } void doSomethingWrong( TFoo(Object) t ) { t.add(new B()); } instance TFoo(A) a; doSomethingWrong( a ); // implicit conversion -- Nicolas Repiquet
Sep 09 2003