Thread overview
Reflection magic: function signatures
Jun 30, 2007
Christopher Wright
Jul 01, 2007
Christopher Wright
June 30, 2007
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!
June 30, 2007
"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;
}


July 01, 2007
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!
July 01, 2007
"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.