Compile-time reflection

Christopher Wright dhasenan at gmail.com
Sun Jul 1 16:52:08 PDT 2007


Kirk McDonald wrote:
<snip>
> The first thing a compile-time reflection mechanism needs is a way to, 
> given a symbol, derive a tuple of the signatures of the function 
> overloads. There is no immediately obvious syntax for this.

Overloads are entirely separate functions. If you go through 
ClassInfo.vtbl, that gives you each overload separately. And the only 
thing overloads have in common are their names. So is this actually 
important?

The annoyance is when you have overloads, how do you refer to the 
correct one?

---
void foo(int i) {}
void foo(int i, char[] str) {}
auto f1 = &foo; // works, but which does it give?
void function(int, char[]) f2 = &foo; // does the right thing
---

That works fine, as long as one person wrote it all specifically for the 
cases it's used in. What if you're sending a function reference to some 
other piece of code that can take any function?

---
void foo(int i) {}
void foo(int i, char[] str) {}
some_other_lib.use_some_function(&foo);
---

Well, you can do the obvious workaround:

---
void function(int) foo_to_send = &foo;
some_other_lib.use_some_function(&foo_to_send);
---

It just bothers me that the only way to specify which reference is by 
assignment to a function reference with a specific signature. I'd prefer 
a syntax more like:
auto f1 = &foo(int, char[]);

Unambiguous, since references don't have an opCall, and shorter. But the 
present workaround is merely annoying, and only mildly at that.

<snip>
> foo.tupleof => Tuple!(void function(), void function(int), void 
> function(int, int, int))

Okay, sounds simple enough, but why do you need each overload? They're 
separate functions; they don't have anything in common, strictly 
speaking, except the name.

> It should be clear, then, that automatically deriving the overloads of a 
> given function is very important. Another piece of information that is 
> useful is whether a given function has default arguments, and how many. 
> The tupleof() syntax can be re-used for this:
> 
> tupleof(foo, void function(int, int, int)) => Tuple!(void function(int, 
> int))

This is interesting. Currently, there's no way to get which arguments 
are omissible.

...
> D does not really have pointers to member functions. It is possible to 
> fake them with some delegate trickery. In particular, there is no way to 
> directly call an alias of a member function. This is important, as I 
> will get to later.

The reason for this is obvious; the compiler rewrites the functions as 
you describe below. I haven't looked, but I'd guess a delegate is 
something like this:
struct delegate (T, R, U...) {
    void* func;
    T obj;
    R opCall(U u) { return *func(obj, u); }
}

...
> A.methodsof => Tuple!(A.bar, A.baz, A.foobar, A.foobaz)
> B.methodsof => Tuple!(A.bar, A.foobar, A.foobaz, B.foo, B.baz)

You can already get this through A.classinfo.vtbl. That contains all 
functions that are valid for the class. You can create a template that 
will determine which functions are inherited, though you cannot say 
which override functions from the base class with any certainty.

> static(A.bar == static) == false
> static(A.bar == final) == false
> static(A.bar == virtual) == true

Currently, there's no way I know of to get this information. The 
is(typeof()) system works with all functions, static or not, whether you 
use them from a type or from an instance.

Hack? Do everything from instance variables. Dunno what to do about 
final, though.

Checking about final stuff matters if you want your program to behave 
differently toward final methods, but nothing in ClassInfo prevents you 
from replacing a final method.



More information about the Digitalmars-d mailing list