Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sat Aug 1 16:12:58 UTC 2020

Progress is moving along nicely with __typeid. Currently all types aside 
from `struct`s, `class`es and `interface`s are supported:


I don't see major issues ahead, so __typeid!(T) should supplant the 
functionality provided by typeid(T) with no or little magic needed on 
the compiler side. It would allow deleting a gnarly part of the compiler 
- so convoluted, in fact, that Walter himself gave up on modularizing it 
and decided to wait for __typeid to be finished so he throws all that 
away in one fell swoop. That's going to be fun.

Reading a bit more about TypeInfo and related stuff, I figure (and 
correct me if I'm wrong) that the entire machinery is currently used for 
the following purposes (only):

* Built-in associative arrays (which ought to be phased out)

* Inform the GC of what it needs to scan (provided in an awkward manner 
though, this is subject to a later discussion)

* Dynamic cast information for class objects

* Object creation for Object.factory() (which also should be phased out)

The API provided by TypeInfo has evolved with these uses in mind, so the 
bulk of it is concerned with primitives for associative arrays such as 
comparison, hashing, and swapping.

The more exciting part is not just replacing the existing functionality, 
but taking it forward. In the future:

* Built-in associative arrays should be templated and make no use of 
typeid at all

* The GC interface should be done through functions, not data 
structures. __typeid!(T) would provide a function scan(T*) that scans 
the given object and all pointers it embeds, transitively. That is easy 
to implement in an efficient manner because the static information about 
T (and therefore its .tupleof) is available when the function is 
generated. Templates, wheee!

* Dynamic cast information for class objects should be kept, perhaps 
made more efficiently than a linear search.

* Object creation primitives should be only provided in an opt-in 
manner. If a class hierarchy wants to expose a factory function, all it 
needs to do is inherit a tag interface:

interface DynamicallyConstructible {}

During the generation of __typeid, the inheritance of this tag is 
detected and the appropriate construction code is generated.

* An important use of __typeid will be to implement Variant "the right 
way". Using the __typeid instead of the unsightly pointer to handler 
function in Variant would go a long way toward simplifying it. the 
__typeid API would be therefore geared toward the needs of that type. 
I've come to the realization that that type is essential for the use of 
D in dynamic contexts. I'd name that type Box, put it in druntime, and 
make it available to Das Besser C.

* Once Box is present, much more is possible. It's not difficult to do 
introspection during compilation on a class object and expose its 
methods at runtime. We'd do that only if another tag interface is 
inherited, again in an opt-in manner:

interface DynamicallyInvokable {}

So it becomes possible to expose objects at runtime, making uses like 
this possible:

// Inside the library
class Widget : DynamicallyInvokable
     int frobnicate(double a, int b) { ... }

// Application that does NOT import the widget module
// Load the typeid(Widget) from a dynamic library
TypeInfo ti = loadType("./mylibrary.so", "widget.Widget");
// Make a widget. Note that we do NOT have the Widget class definition!
Object w = ti.make();
// Call Widget.frobnicate an get its result
Box result = ti.invoke(w, "frobnicate", 3.14, 42);
// The Box contains an int
int x = result.get!int;

All of this is well within reach, but it's a fair amount of work.

More information about the Digitalmars-d mailing list