Thread overview
An experimental reflection library
Aug 04, 2004
s31552
Aug 04, 2004
parabolis
Aug 04, 2004
Garett Bass
Aug 04, 2004
shinichiro.h
Aug 04, 2004
Mickey Finn
August 04, 2004
I implemented a reflection library in D experimentally. Because now DMD compiler does not support reflection, my implementation is not smart.

If you want to use my reflection library, you should prepare a file which contains symbol table using /MAP linker option. (or nm(1) command on Unix). The library read the symbol table from the file and demangle symbols. Since the symbols of D language has type infomations, the symbol table is enough to implement reflection.

You can download the library here: http://user.ecc.u-tokyo.ac.jp/~s31552/wp/d/reflection.tar.bz2

The file contains four files, reflection.d is the main library code,
test_reflection.d is a test code, and build_*.sh are build
scripts. The library supports both DMD and GDC. I check this library
with DMD&Windows, DMD&Linux, GDC&Linux, GDC&MacOSX. Please note that
the library has a lot of todos to simulate real reflection and the
library will be no use if DMD supports reflection.

A sample code of my library is the following:

import reflection;

class Test {
    void test_func1() { std.stream.stdout.writeLine("func1"); }
    void test_func2() { std.stream.stdout.writeLine("func2"); }
    void test_func3() { std.stream.stdout.writeLine("func3"); }
    int test_ignore1() { std.stream.stdout.writeLine("error"); return 0; }
    void test_ignore2(int i) { std.stream.stdout.writeLine("error"); }
    void ignore() { std.stream.stdout.writeLine("error"); }
}

int main(char[][] args) {
    Reflection.init(args[1]);

    Test test = new Test();
    Class c = Class.forName("test_reflection.Test");
    if (c !== null) {
        foreach (Method m; c.methods) {
            std.stream.stdout.writeLine("found: " ~ m.name);
            if (m.name.length >= 4 && m.name[0..4] == "test" &&
                m.type.func.ret.builtin.type == Builtin.Type.VOID &&
                m.type.func.args.length == 0)
            {
                std.stream.stdout.writeLine("invoke: " ~ m.name);
                (cast(void (*)(Test))m.address)(test);
            }
        }
    }
    else {
        std.stream.stdout.writeLine("cannot get test class");
    }
}

The above program iterates methods of "Test" class, and invoke the each method when the name of the method starts with "test" and the method returns void and requires no arguments. When you run the program, you'll obtain the following message in stdout:

found: ignore
found: test_func1
invoke: test_func1
func1
found: test_func2
invoke: test_func2
func2
found: test_func3
invoke: test_func3
func3
found: test_ignore1
found: test_ignore2

Please comment if you are interested in this library.

------------------
 shinichiro.h
  s31552@mail.ecc.u-tokyo.ac.jp
  http://user.ecc.u-tokyo.ac.jp/~s31552/wp/
August 04, 2004
s31552@mail.ecc.u-tokyo.ac.jp wrote:

> I implemented a reflection library in D experimentally. Because now
> DMD compiler does not support reflection, my implementation is not
> smart.


> Please comment if you are interested in this library.
> 

Well done! I was just discussing the possibility of reflection and I believe the thing you want to see is for ClassInfo to tell you what kinds of fields the instantiated object contains and in what order they appear.

Is there anything else that you would need for full reflection?
August 04, 2004
I must admit, I haven't entirely grokked your reflection implementation.  I just want to add that, for serialization purposes, it would also be very handy if there were some means of reflecting upon a class's member variables.  This would make it easy to create templates that could serialize/deserialize an object to/from a stream.

Bravo,
Garett


August 04, 2004
> Well done! I was just discussing the possibility of reflection and I believe the thing you want to see is for ClassInfo to tell you what kinds of fields the instantiated object contains and in what order they appear.
> 
> Is there anything else that you would need for full reflection?

Yes, I think field infomation is needed. Object serialization is one of the big motivation for reflection.

And if I continue to consider the demangle-base reflection, the name mangling rule of D language is not good now. We cannot distinguish C_f.C and C!(float):

class C_f { class C {} }
class C(T) {}
C!(float) c;
// They have the same "_Class_4test3C_f1C" symbol.
// So this code causes link error.

And I think implementing a user-friendly interface of constructor is difficult. We can construct objects manually with _d_newclass function. The code which call constructor will be like following:

class Test { this(int i) {} }

Class c = Class.forName(`test.Test`);
void* ctor = c.getCtor();
ClassInfo ci = c.getClassInfo();
Object otmp = _d_newclass(ci);
Object o = (cast(Object(*)(ClassInfo, int))ctor)(otmp, 1);

It is complicated.

------------------
 shinichiro.h
August 04, 2004
Nice!

<s31552@mail.ecc.u-tokyo.ac.jp> wrote in message news:20040804140404.05de4e3a.s31552@mail.ecc.u-tokyo.ac.jp...
> I implemented a reflection library in D experimentally. Because now DMD compiler does not support reflection, my implementation is not smart.
>
> If you want to use my reflection library, you should prepare a file which contains symbol table using /MAP linker option. (or nm(1) command on Unix). The library read the symbol table from the file and demangle symbols. Since the symbols of D language has type infomations, the symbol table is enough to implement reflection.
>
> You can download the library here: http://user.ecc.u-tokyo.ac.jp/~s31552/wp/d/reflection.tar.bz2
>
> The file contains four files, reflection.d is the main library code,
> test_reflection.d is a test code, and build_*.sh are build
> scripts. The library supports both DMD and GDC. I check this library
> with DMD&Windows, DMD&Linux, GDC&Linux, GDC&MacOSX. Please note that
> the library has a lot of todos to simulate real reflection and the
> library will be no use if DMD supports reflection.
>
> A sample code of my library is the following:
>
> import reflection;
>
> class Test {
>     void test_func1() { std.stream.stdout.writeLine("func1"); }
>     void test_func2() { std.stream.stdout.writeLine("func2"); }
>     void test_func3() { std.stream.stdout.writeLine("func3"); }
>     int test_ignore1() { std.stream.stdout.writeLine("error"); return 0; }
>     void test_ignore2(int i) { std.stream.stdout.writeLine("error"); }
>     void ignore() { std.stream.stdout.writeLine("error"); }
> }
>
> int main(char[][] args) {
>     Reflection.init(args[1]);
>
>     Test test = new Test();
>     Class c = Class.forName("test_reflection.Test");
>     if (c !== null) {
>         foreach (Method m; c.methods) {
>             std.stream.stdout.writeLine("found: " ~ m.name);
>             if (m.name.length >= 4 && m.name[0..4] == "test" &&
>                 m.type.func.ret.builtin.type == Builtin.Type.VOID &&
>                 m.type.func.args.length == 0)
>             {
>                 std.stream.stdout.writeLine("invoke: " ~ m.name);
>                 (cast(void (*)(Test))m.address)(test);
>             }
>         }
>     }
>     else {
>         std.stream.stdout.writeLine("cannot get test class");
>     }
> }
>
> The above program iterates methods of "Test" class, and invoke the each method when the name of the method starts with "test" and the method returns void and requires no arguments. When you run the program, you'll obtain the following message in stdout:
>
> found: ignore
> found: test_func1
> invoke: test_func1
> func1
> found: test_func2
> invoke: test_func2
> func2
> found: test_func3
> invoke: test_func3
> func3
> found: test_ignore1
> found: test_ignore2
>
> Please comment if you are interested in this library.
>
> ------------------
>  shinichiro.h
>   s31552@mail.ecc.u-tokyo.ac.jp
>   http://user.ecc.u-tokyo.ac.jp/~s31552/wp/