www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Reflection magic: function signatures

reply Christopher Wright <dhasenan gmail.com> writes:
Hey,

I've been fooling around with the idea of creating a mocks library. The 
important thing that I need to do to get that is to create functions 
that can be swapped in for any functions that the mocked class or 
interface has.

If I could implicitly cast primitives to objects, I could just write one 
variadic function taking a series of Objects. (I'd have to write a 
template to create a bunch of them that I could distinguish, but that's 
a minor issue.)

If I could create functions at runtime, I could just go with runtime 
everything. But that's not possible in a compiled language. (Or at 
least, it'd be extremely ugly, and not widely useful, so the compiler 
cruft wouldn't be worth it. In interpreted languages, the compiler cruft 
is much smaller, so it's worth it.)

I could try using templates to manufacture all reasonable function 
prototypes, but even assuming that I can safely and implicitly cast 
everything to real or Object, that's going to be O(2**n) functions to 
create to support n arguments. Moreover, that still only supports 
functions of a constant length.

If I could do opImplicitCast_r and provide an argument corresponding to 
the type to cast from, well, that would allow me to autobox all 
arguments and just use varargs to handle everything.

opAssign isn't used for function arguments, or else Bob'd be my uncle.

As far as I can tell, I can't suss out a function signature at compile 
time with the current reflection systems. Flectioned seems to require a 
pointer to the function, and that won't exist until the function has 
been compiled.

The best I can do is something like:
---
RandomClass mock = Mock!(RandomClass); // don't use auto or badness results
Expect.Call(&(mock.method)).Arguments!(3, some_object, 9.2f);
---

Then I'd have to use the Arguments template (provided the arguments are 
CTFE-friendly) to get the types and create a valid overload. But what if 
the arguments aren't valid at compile time?

---
RandomClass mock = Mock!(RandomClass); // don't use auto or badness results

Expect.Call(&(mock.method)).Overload!(int, SomeClass, 
float).Arguments(3, some_object, 9.2f).ReturnType!(int).Return(4);

Mocks.Replay(); // functions are actually reassigned here
---

In this case, creating the functions should be simple enough:
---
T function (U) CreateFunction(T, U...)() {
     T func(U u) { return T.init; } // probably just does first element; 
need mixin
     return &func;
}
---

At this point, at runtime, I have to use Flectioned to find the method 
to overwrite, and I can then tell the person whether they got the mock's 
signature right.

This means that the compiler can't warn the user when something goes 
wrong. That's not a good thing, but I can write that code myself, if I 
have to. But it delays warnings and increases time to finding errors.

Any suggestions as to finding a function signature at compile time? That 
would be the best solution.

Thanks!
Jun 30 2007
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:f65pa1$1rt3$1 digitalmars.com...
 As far as I can tell, I can't suss out a function signature at compile 
 time with the current reflection systems. Flectioned seems to require a 
 pointer to the function, and that won't exist until the function has been 
 compiled.

But.. but you did this:
 ---
 T function (U) CreateFunction(T, U...)() {
     T func(U u) { return T.init; } // probably just does first element; 
 need mixin
     return &func;
 }
 ---

The function signature is just the return type + parameter type tuple, no? import std.traits; T function(U) CreateFunction(FuncType, T = ReturnType!(FuncType), U = ParameterTypeTuple!(FuncType))() { // static so we can return it static T func(U u) { return T.init; } return &func; }
Jun 30 2007
parent reply Christopher Wright <dhasenan gmail.com> writes:
Jarrett Billingsley wrote:
 "Christopher Wright" <dhasenan gmail.com> wrote in message 
 news:f65pa1$1rt3$1 digitalmars.com...
 As far as I can tell, I can't suss out a function signature at compile 
 time with the current reflection systems. Flectioned seems to require a 
 pointer to the function, and that won't exist until the function has been 
 compiled.

But.. but you did this:
 ---
 T function (U) CreateFunction(T, U...)() {
     T func(U u) { return T.init; } // probably just does first element; 
 need mixin
     return &func;
 }
 ---

The function signature is just the return type + parameter type tuple, no? import std.traits; T function(U) CreateFunction(FuncType, T = ReturnType!(FuncType), U = ParameterTypeTuple!(FuncType))() { // static so we can return it static T func(U u) { return T.init; } return &func; }

My god, you're a genius!
Jun 30 2007
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:f66s11$11t4$1 digitalmars.com...

 My god, you're a genius!

No, D's just amazing ;) And I'm writing a library for my scripting language now to automatically generate shim functions to make it easier to bind native functions to the scripting interpreter. It's heavily inspired by Pyd (www.dsource.org/projects/pyd), which does a lot of what you seem to want to do, so you can have a look at that for some more function wrapping examples. Or, you know, look at my library too (www.dsource.org/projects/minid, look in trunk/minid/bind.d in the repo). BTW when you use a type tuple (either a T... parameter ot something like the result of ParameterTypeTuple) as a parameter list or so, it does use all the elements, no mixins required.
Jun 30 2007