www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Like getClass()

reply bearophile <bearophileHUGS lycos.com> writes:
I'm trying to learn more about D object system (this is derived from a
discussion I've had in #D), I have problems fully understanding some of the
things the D docs say. This is a little Java program for reference:

// Java code
import java.util.*;
interface FooBar {}
class Foo implements FooBar {}
class Bar implements FooBar {}
public class test {
    public static void main(String[] args) {
        ArrayList<FooBar> things = new ArrayList<FooBar>();
        things.add(new Bar());
        things.add(new Foo());
        for (FooBar thing : things)
            System.out.println(thing.getClass());
    }
}
/*
Output of the Java version:
class Bar
class Foo
*/


I have tried to translate it to D, and I have failed so far, this code compiles
and runs, but I'd like to see the original class names in the output:

// D code
import std.stdio;
interface FooBar {}
class Foo : FooBar {}
class Bar : FooBar {}
void main() {
    FooBar[] things;
    things ~= new Bar;
    things ~= new Foo;
    foreach (thing; things)
        writefln(typeid(typeof(thing)));
}
/*
Output of the D version:
test.FooBar
test.FooBar
*/

As you can see in D I am not able yet to find the class of the objects inserted
into the 'things' array. Do you have suggestions? (I'd like to do this to sort
the 'things' array according to the class).

Bye and thank you,
bearophile
Sep 27 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 12:15:07 -0400,
bearophile wrote:
 // D code
 import std.stdio;
 interface FooBar {}
 class Foo : FooBar {}
 class Bar : FooBar {}
 void main() {
     FooBar[] things;
     things ~= new Bar;
     things ~= new Foo;
     foreach (thing; things)
         writefln(typeid(typeof(thing)));
 }
 /*
 Output of the D version:
 test.FooBar
 test.FooBar
 */
typeof() gives a static type of an object. But I was sure that thing.classinfo.name should have referred to the dynamic type name. Apparently it's not the case, it prints the same test.FooBar as in your test.
Sep 27 2008
parent reply bearophile <bearophileHUGS lycos.com> writes:
Sergey Gromov:
 typeof() gives a static type of an object.  But I was sure that 
 thing.classinfo.name should have referred to the dynamic type name.  
 Apparently it's not the case, it prints the same test.FooBar as in your 
 test.
Thank you for your answer. So the question here is: is the information I need present at runtime somewhere? If the answer is true, then the problem is how to get it. I think this page of the docs may be improved with few graphical drawings of the data structures it talks about (with lines that show where the pointers point to at runtime in a simple example); such images can probably give all or most of the answers I was looking for: http://www.digitalmars.com/d/1.0/phobos/object.html Bye, bearophile
Sep 27 2008
parent reply torhu <no spam.invalid> writes:
bearophile wrote:
 Sergey Gromov:
 typeof() gives a static type of an object.  But I was sure that 
 thing.classinfo.name should have referred to the dynamic type name.  
 Apparently it's not the case, it prints the same test.FooBar as in your 
 test.
Thank you for your answer. So the question here is: is the information I need present at runtime somewhere? If the answer is true, then the problem is how to get it.
obj.classinfo doesn't work the way you'd expect with an interface, but it works when you've got an object: writefln((cast(Object)thing).classinfo.name); This does look ugly, though.
Sep 27 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
torhu:
 writefln((cast(Object)thing).classinfo.name);
It works, thank you very much. Now I'll try to use it for the original purposes...... I'll have to work some more. I'll show here the results if I finish.
 This does look ugly, though.
Well, better ugly than not having any way to solve the problem :-) Bye, bearophile
Sep 27 2008
prev sibling next sibling parent bearophile <bearophileHUGS lycos.com> writes:
I have solved my original problem :-) I show here the code for reference:

This is the original Python code, that sorts a python list (array) of objects
according to their class:


class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    def draw(self):
        print "Drawing Circle at (%s,%s) with radius %s" % (self.x, self.y,
self.radius)

class Square:
    def __init__(self, x, y, width):
        self.x = x
        self.y = y
        self.width = width
    def draw(self):
        print "Drawing Square at (%s,%s) with width %s" % (self.x, self.y,
self.width)

if __name__ == "__main__":

    shapes = [Circle(1,1,5), Square(2,4,3), Circle(3,3,7), Square(4,2,4)]
    shapes.sort(key=lambda s: order.index(s.__class__))
    for shape in shapes:
        shape.draw()




This is my best D translation so far, I am happy enough:

import std.stdio: writefln;
import d.func: sort, findPos;

interface Shape {
    void draw();
}

class Circle : Shape {
    float x, y, radius;
    this(float x, float y, float radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    void draw() {
        writefln("Drawing Circle at (%s,%s) with radius %s", x, y, radius);
    }
}

class Square : Shape {
    float x, y, width;
    this(float x, float y, float width) {
        this.x = x;
        this.y = y;
        this.width = width;
    }
    void draw() {
        writefln("Drawing Square at (%s,%s) with width %s", x, y, width);
    }
}

void main() {
    auto order = [Circle.classinfo, Square.classinfo]; // the order in which to
draw the shapes

    auto shapes = [cast(Shape)(new Circle(1,1,5)), new Square(2,4,3),
                   new Circle(3,3,7), new Square(4,2,4)];

    shapes.sort((Shape s){ return findPos((cast(Object)s).classinfo, order); });

    foreach (shape; shapes)
        shape.draw();
}

It uses sort() and findPos(), but similar functions are probably present in
Tango too, the first sorts an array according to a key function, and the second
gives the index of an item into an iterable or -1 if absent.

If the 'order' array becomes too much long it can of course be replaced by an
associative array, that gives a faster lookup.

Bye,
bearophile
Sep 27 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 19:50:18 +0200,
torhu wrote:
 obj.classinfo doesn't work the way you'd expect with an interface, but 
 it works when you've got an object:
 
 writefln((cast(Object)thing).classinfo.name);
Thanks for the tip! It seems like classinfo of an interface contains information about the most derived interface implemented in an object: interface a {} interface aa : a {} class A : aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.aa". Though this functionality is of questionable usefulness (has anybody used it for something?) and besides it's broken: interface a {} interface aa : a {} class A : a, aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.a" even though A implements the "aa" interface as well.
Sep 27 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Sergey Gromov wrote:
 Sat, 27 Sep 2008 19:50:18 +0200,
 torhu wrote:
 obj.classinfo doesn't work the way you'd expect with an interface, but 
 it works when you've got an object:

 writefln((cast(Object)thing).classinfo.name);
Thanks for the tip! It seems like classinfo of an interface contains information about the most derived interface implemented in an object: interface a {} interface aa : a {} class A : aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.aa". Though this functionality is of questionable usefulness (has anybody used it for something?) and besides it's broken: interface a {} interface aa : a {} class A : a, aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.a" even though A implements the "aa" interface as well.
Since .classinfo is essentially a virtual call, and interface vtbls are filled from the class's vtbl, it shouldn't in theory be terribly difficult to change this. Of course, I'd have to check the dmd source to be sure.
Sep 27 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 27 Sep 2008 19:21:56 -0400,
Christopher Wright wrote:
 Sergey Gromov wrote:
 Sat, 27 Sep 2008 19:50:18 +0200,
 torhu wrote:
 obj.classinfo doesn't work the way you'd expect with an interface, but 
 it works when you've got an object:

 writefln((cast(Object)thing).classinfo.name);
Thanks for the tip! It seems like classinfo of an interface contains information about the most derived interface implemented in an object: interface a {} interface aa : a {} class A : aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.aa". Though this functionality is of questionable usefulness (has anybody used it for something?) and besides it's broken: interface a {} interface aa : a {} class A : a, aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.a" even though A implements the "aa" interface as well.
Since .classinfo is essentially a virtual call, and interface vtbls are filled from the class's vtbl, it shouldn't in theory be terribly difficult to change this. Of course, I'd have to check the dmd source to be sure.
I'd say it's a bug. Classinfo is for runtime class, and runtime class can never be an interface.
Sep 27 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Sergey Gromov:
 I'd say it's a bug.  Classinfo is for runtime class, and runtime class 
 can never be an interface.
It seems all my questions in this newsgroup end with the discovery of another possible (and often real) bug ;-] If it's not a bug, then I think a nicer syntax may be appreciable. In such thing Java may show some better syntax (well, better method name essentially, so better API and not really better syntax). Bye and thank you, bearophile
Sep 27 2008
prev sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Sun, 28 Sep 2008 04:42:15 +0400, Sergey Gromov <snake.scaly gmail.com>  
wrote:

 Sat, 27 Sep 2008 19:21:56 -0400,
 Christopher Wright wrote:
 Sergey Gromov wrote:
 Sat, 27 Sep 2008 19:50:18 +0200,
 torhu wrote:
 obj.classinfo doesn't work the way you'd expect with an interface,  
but
 it works when you've got an object:

 writefln((cast(Object)thing).classinfo.name);
Thanks for the tip! It seems like classinfo of an interface contains information about the most derived interface implemented in an object: interface a {} interface aa : a {} class A : aa {} void main() { writefln((cast(a) new A).classinfo.name); } prints "test.aa". Though this functionality is of questionable usefulness (has anybody used it for something?) and besides it's
broken:
 interface a {}
 interface aa : a {}
 class A : a, aa {}
 void main() {
     writefln((cast(a) new A).classinfo.name);
 }

 prints "test.a" even though A implements the "aa" interface as well.
Since .classinfo is essentially a virtual call, and interface vtbls are filled from the class's vtbl, it shouldn't in theory be terribly difficult to change this. Of course, I'd have to check the dmd source to be sure.
I'd say it's a bug. Classinfo is for runtime class, and runtime class can never be an interface.
Do all IUknown interface instances have classinfo member? That's not a bug if they don't. That's the same reason why interfaces can't be casted to Object.
Sep 28 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 29 Sep 2008 01:12:45 +0400,
Denis Koroskin wrote:
 On Sun, 28 Sep 2008 04:42:15 +0400, Sergey Gromov <snake.scaly gmail.com>  
 wrote:
 
 Sat, 27 Sep 2008 19:21:56 -0400,
 Christopher Wright wrote:
 Since .classinfo is essentially a virtual call, and interface vtbls are
 filled from the class's vtbl, it shouldn't in theory be terribly
 difficult to change this. Of course, I'd have to check the dmd source to
 be sure.
I'd say it's a bug. Classinfo is for runtime class, and runtime class can never be an interface.
Do all IUknown interface instances have classinfo member? That's not a bug if they don't. That's the same reason why interfaces can't be casted to Object.
IUnknown cannot be a D interface because it does not comply with D ABI. IUnknown cannot have any .classinfo. Iunknown cannot be cast to anything except by the means of QueryInterface(). You've chosen the wrong example. Only D interfaces can have .classinfo. You cannot instantiate a D interface, only a class. Therefore whenever you get a reference it's either null or refers to an instantiated *class* which definitely has its one and only correct .classinfo.
Sep 28 2008
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Mon, 29 Sep 2008 03:02:58 +0400,
Sergey Gromov wrote:
 Mon, 29 Sep 2008 01:12:45 +0400,
 Denis Koroskin wrote:
 On Sun, 28 Sep 2008 04:42:15 +0400, Sergey Gromov <snake.scaly gmail.com>  
 wrote:
 
 Sat, 27 Sep 2008 19:21:56 -0400,
 Christopher Wright wrote:
 Since .classinfo is essentially a virtual call, and interface vtbls are
 filled from the class's vtbl, it shouldn't in theory be terribly
 difficult to change this. Of course, I'd have to check the dmd source to
 be sure.
I'd say it's a bug. Classinfo is for runtime class, and runtime class can never be an interface.
Do all IUknown interface instances have classinfo member? That's not a bug if they don't. That's the same reason why interfaces can't be casted to Object.
IUnknown cannot be a D interface because it does not comply with D ABI.
My apologies. I didn't know that IUnknown were directly supported by a compiler as a special case of a super-interface. Everything you say seems to be relevant now.
Oct 02 2008
parent "Tim M" <a b.com> writes:
Did you ever figure out what you were trying to solve. typeof is evaluated  
at compile time so not very useful. If it helps you or anyone else reading  
this some sample code you may find usefull:

module test;

import tango.io.Stdout;

Object dupClass(Object o)
{
         Object newObj = o.classinfo.create;
         return newObj;
}

class Animal
{
         this(char[] name)
         {
                 this.name = name;
         }
         this()
         {
                 //
         }
         public char[] name;
         char[] kind()
         {
                 return "animal";
         }
}

class Dog : Animal
{
         this(char[] name)
         {
                 super(name);
         }
         this()
         {
                 super();
         }
         char[] kind()
         {
                 return "Dog";
         }
}

class Cat : Animal
{
         this(char[] name)
         {
                 super(name);
         }
         this()
         {
                 super();
         }
         char[] kind()
         {
                 return "Cat";
         }
}

int main(char[][] args)
{
         Animal[] animals;

         Dog dog = new Dog("Scooby");
         Cat cat = new Cat("Garfield");

         /* create a new object of the same class as dog */
         animals ~= cast(Animal) dupClass(dog);

         /* new dog needs a name */
         animals[0].name = "Lassy";


         /* add the others to the array */
         animals ~= dog;
         animals ~= cat;

         foreach(Animal anim; animals)
         {
                 Stdout(anim.kind~" : "~anim.name).newline;
         }

         return 0;
}



On Fri, 03 Oct 2008 14:44:11 +1300, Sergey Gromov <snake.scaly gmail.com>  
wrote:

 Mon, 29 Sep 2008 03:02:58 +0400,
 Sergey Gromov wrote:
 Mon, 29 Sep 2008 01:12:45 +0400,
 Denis Koroskin wrote:
 On Sun, 28 Sep 2008 04:42:15 +0400, Sergey Gromov  
<snake.scaly gmail.com>
 wrote:

 Sat, 27 Sep 2008 19:21:56 -0400,
 Christopher Wright wrote:
 Since .classinfo is essentially a virtual call, and interface  
vtbls are
 filled from the class's vtbl, it shouldn't in theory be terribly
 difficult to change this. Of course, I'd have to check the dmd  
source to
 be sure.
I'd say it's a bug. Classinfo is for runtime class, and runtime
class
 can never be an interface.
Do all IUknown interface instances have classinfo member? That's not
a bug
 if they don't. That's the same reason why interfaces can't be casted  
to
 Object.
IUnknown cannot be a D interface because it does not comply with D ABI.
My apologies. I didn't know that IUnknown were directly supported by a compiler as a special case of a super-interface. Everything you say seems to be relevant now.
Oct 17 2008