www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Function pointer argument to function template

reply Kirk McDonald <kirklin.mcdonald gmail.com> writes:
By way of example, take this simple function template (taken from an 
example in the spec):

template Foo(T, U=T*) {
     void Foo(T t) {
         U p;
         // ...
     }
}

int x;
Foo(x); // T is int, U is int*

Say I have a template function that takes a funtion pointer (or function 
object, I suppose) as an argument.

template Bar(T) {
     void Bar(T t) {
         t();
     }
}

void bar() {
     writef("Blah blah\n");
}

Bar(&bar);

Simple enough. But I want to deduce the return type of this function 
pointer, and make it the default value of a second template parameter.

template Bar(T, U=<something>) {
     U Bar(T t) {
         return t();
     }
}

Now, it's easy enough to explicitly instantiate this template, but I 
want to use this with the oh-so-useful implicit function template 
instantiation, so I could write e.g.:

int bar() {
     return 7;
}

writef("value is: %d\n", Bar(&bar));

Is there something big and obvious stopping this that I'm not seeing?

-Kirk McDonald
May 30 2006
next sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Hey there :)

I came up with this:

----

import std.stdio;

template Deref(T) {
         alias typeof(*T) Deref;
}

template RetType(T) {
         static if (is(Deref!(T) U == function)) {
                 alias U RetType;
         } else static assert (false);
}

template Bar(T, U=RetType!(T)) {
         U Bar(T t) {
                 writefln(typeid(U));

                 // do something :P
                 return U.init;
         }
}


cfloat func(int a, float b) {
         return 1.f + 0i;
}


void main() {
         writefln(Bar(&func));
}


--
Tomasz Stachowiak  /+ a.k.a. h3r3tic +/
May 30 2006
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Ah! The IsExpression! I hadn't remembered that at all from when I read 
the spec. This will do nicely, thanks!

-Kirk McDonald

Tom S wrote:
 Hey there :)
 
 I came up with this:
 
 ----
 
 import std.stdio;
 
 template Deref(T) {
         alias typeof(*T) Deref;
 }
 
 template RetType(T) {
         static if (is(Deref!(T) U == function)) {
                 alias U RetType;
         } else static assert (false);
 }
 
 template Bar(T, U=RetType!(T)) {
         U Bar(T t) {
                 writefln(typeid(U));
 
                 // do something :P
                 return U.init;
         }
 }
 
 
 cfloat func(int a, float b) {
         return 1.f + 0i;
 }
 
 
 void main() {
         writefln(Bar(&func));
 }
 
 
 -- 
 Tomasz Stachowiak  /+ a.k.a. h3r3tic +/

May 30 2006
prev sibling next sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Tom S wrote:
 Hey there :)
 
 I came up with this:
 
 ----
 
 import std.stdio;
 
 template Deref(T) {
         alias typeof(*T) Deref;
 }
 
 template RetType(T) {
         static if (is(Deref!(T) U == function)) {
                 alias U RetType;
         } else static assert (false);
 }
 
 template Bar(T, U=RetType!(T)) {
         U Bar(T t) {
                 writefln(typeid(U));
 
                 // do something :P
                 return U.init;
         }
 }
 
 
 cfloat func(int a, float b) {
         return 1.f + 0i;
 }
 
 
 void main() {
         writefln(Bar(&func));
 }
 
 
 -- 
 Tomasz Stachowiak  /+ a.k.a. h3r3tic +/

Wow. That's... so much nicer than mine. I'm not even going to post my comparatively hideous solution :p May as well ask this: do you know a simple way to infer both the number and type of a function's arguments? I have a set of templates that can do this, but they're somewhat ugly. -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 30 2006
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Daniel Keep wrote:
 May as well ask this: do you know a simple way to infer both the number
 and type of a function's arguments?  I have a set of templates that can
 do this, but they're somewhat ugly.
 
 	-- Daniel
 

In C++, at least (which, yes yes, is not D, but it is similar enough in this respect), you had to do something like the following: template Foo(Fn) { void Foo(Fn fn) { fn(); } } template Foo(Fn, A0) { void Foo(Fn fn, A0 a0) { fn(a0); } } template Foo(Fn, A0, A1) { void Foo(Fn fn, A0 a0, A1 a1) { fn(a0, a1); } } // Ad absurdum, for as many args as you wish to support. Ugly, no? Try looking through the Boost::Tuple library sometime for the ultimate real-world example of this. (It supports this for up to 10 arguments.) What would totally nip this thing in the bud would be variadic templates, where something like this would work: template Foo(Fn, ...) { void Foo(Fn fn, ...) { fn(<pass_args_somehow>); } } I think I saw a post on this newsgroup somewhere where Walter said he'd like to see this in D... ah! Here's the quote: Walter Bright wrote on Feb 9, 2006:
 I know it'd be cool, and I want to do it. But it's a 2.0 thing.

-Kirk McDonald
May 31 2006
prev sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Daniel Keep wrote:
 May as well ask this: do you know a simple way to infer both the number
 and type of a function's arguments?  I have a set of templates that can
 do this, but they're somewhat ugly.

Unfortunately I've got no idea as how to do it in a way that would detect more than primitive types as the arguments. My guess is that D would need more metainfo for functions or a means of converting a "type" to type (compile-time string to a compile-time identifier). -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
May 31 2006
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Tom S wrote:
 Daniel Keep wrote:
 May as well ask this: do you know a simple way to infer both the number
 and type of a function's arguments?  I have a set of templates that can
 do this, but they're somewhat ugly.

Unfortunately I've got no idea as how to do it in a way that would detect more than primitive types as the arguments. My guess is that D would need more metainfo for functions or a means of converting a "type" to type (compile-time string to a compile-time identifier).

Actually, it's quite simple, if utterly evil. It involves abusing IFTI in the most gruesome manner imaginable... Detecting the number of arguments looks like this: # typedef uint Arglen0 = 0; # typedef uint Arglen1 = 1; # typedef uint Arglen2 = 2; # // and so on... # # template # ArglenT(Tr) # { # Arglen0 # ArglenT(Tr function() fn) { assert(false); } # } # # template # ArglenT(Tr, Ta1) # { # Arglen1 # ArglenT(Tr function(Ta1) fn) { assert(false); } # } # # template # ArglenT(Tr, Ta1, Ta2) # { # Arglen2 # ArglenT(Tr function(Ta1, Ta2) fn) { assert(false); } # } # # // Repeat ad absurdum # # template # ArglenConvT(T) # { # const uint ArglenConvT = T.init; # } # # template # NumberOfArgsT(Tf) # { # private Tf fptr; # alias typeof(ArglenT(fptr)) type; # } # # public # template # NumberOfArgs(Tf) # { # const uint NumberOfArgs = ArglenConvT!(NumberOfArgsT!(Tf).type); # } What do you think of that sneaky use of typedefs? ^_^ Deducing the return type, and argument types is pretty similar (except instead of using ArglenN, you use Tr for the return type or Ta, Tb, Tc, etc for the argument types). The neat upshot of this is that despite using IFTI, I don't actually think those functions ever get instantiated; they certainly never run, which is kinda cool. Thankfully, I have a nifty little Python script that saves me having to actually *type* all that out. I give it a maximum number of arguments, and it goes off and generates the template for me. Evil was never so easy! I actually came up with this because I want to find a way to do Boost-style Python embedding. So you can just use PyWrap(someFn) to generate a Python wrapper for any old D function. *drools at the thought of D+Python* Oops *mops up puddle* -- Daniel -- Unlike Knuth, I have neither proven or tried the above; it may not even make sense. v2sw5+8Yhw5ln4+5pr6OFPma8u6+7Lw4Tm6+7l6+7D i28a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP http://hackerkey.com/
May 31 2006
next sibling parent Kirk McDonald <kirklin.mcdonald gmail.com> writes:
Daniel Keep wrote:
 I actually came up with this because I want to find a way to do
 Boost-style Python embedding.  So you can just use PyWrap(someFn) to
 generate a Python wrapper for any old D function.
 
 *drools at the thought of D+Python*  Oops *mops up puddle*
 
 	-- Daniel
 

Ha! Why do you think I'm asking about this? Also note my earlier thread asking about the state of the Python/D API. I would love to see your code for PyWrap. I've got most of a wrapper around PyObject already. The biggest thing missing (beyond finishing wrapping Python's object interface, which is pretty trivial) is a translator between Python and D exceptions, which requires something like PyWrap to truly function (so it can catch any uncaught D exceptions and safely pass them back into Python). (Yes, this IS how Boost.Python does it, if you're wondering.) :-) -Kirk McDonald
May 31 2006
prev sibling parent reply Tom S <h3r3tic remove.mat.uni.torun.pl> writes:
Daniel Keep wrote:
 Actually, it's quite simple, if utterly evil.  It involves abusing IFTI
 in the most gruesome manner imaginable...
 
 Detecting the number of arguments looks like this:
 
 ( ... )
 

Neat !! I didn't realize IFTI could do that :D
 What do you think of that sneaky use of typedefs?  ^_^  Deducing the
 return type, and argument types is pretty similar (except instead of
 using ArglenN, you use Tr for the return type or Ta, Tb, Tc, etc for the
 argument types).

That's not necessary :>
 The neat upshot of this is that despite using IFTI, I don't actually
 think those functions ever get instantiated; they certainly never run,
 which is kinda cool.

AFAICS, they are instantiated, but never called so the linker or even the compiler might optimize them away :)
 Thankfully, I have a nifty little Python script that saves me having to
 actually *type* all that out.  I give it a maximum number of arguments,
 and it goes off and generates the template for me.  Evil was never so easy!

Hehehe, I use it as well for other sorts of stuff, where I have to generate lots of such code that would be handled by variadic templates if we had them ;) How about taking it a step further and doing something like: ---- import std.stdio; struct FuncMeta(int NumArgs, Ret, T0=void, T1=void /+, and, so, on+/) { alias FuncMeta Meta; static const int numArgs = NumArgs; alias Ret RetType; alias T0 Arg0Type; // these might use static if as well alias T1 Arg1Type; /+ ... and so on +/ } template funcInfo(Ret) { FuncMeta!(0, Ret) funcInfo(Ret function() x) { assert(false); }; } template funcInfo(Ret, T0) { FuncMeta!(1, Ret, T0) funcInfo(Ret function(T0) x) { assert(false); }; } template funcInfo(Ret, T0, T1) { FuncMeta!(2, Ret, T0, T1) funcInfo(Ret function(T0, T1) x) { assert(false); }; } /+ ... template funcInfo(Ret, T0, T1, ..., Tn) { ... } +/ struct Foo {} void fooFunc(Foo a, float b) {} int barFunc(cfloat x) {} void main(char[][] args) { writefln("Number of args of main: ", funcInfo(&main).numArgs); writefln("\nfunc foo:"); alias typeof(funcInfo(&fooFunc)) FooMeta; writefln(FooMeta.numArgs); writefln(typeid(FooMeta.RetType)); writefln(typeid(FooMeta.Arg0Type)); writefln(typeid(FooMeta.Arg1Type)); writefln("\nfunc bar:"); alias typeof(funcInfo(&barFunc)) BarMeta; writefln(BarMeta.numArgs); writefln(typeid(BarMeta.RetType)); writefln(typeid(BarMeta.Arg0Type)); static if (BarMeta.numArgs >= 2) { writefln(typeid(BarMeta.Arg1Type)); } else { writefln("bar doesn't have Arg1Type"); } } Note that the ArgXType could be static if'fed away so one would get an error when trying to access them instead of having them alias to void. -- Tomasz Stachowiak /+ a.k.a. h3r3tic +/
May 31 2006
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Tom S skrev:
 Daniel Keep wrote:
 Actually, it's quite simple, if utterly evil.  It involves abusing IFTI
 in the most gruesome manner imaginable...

 Detecting the number of arguments looks like this:

 ( ... )

Neat !! I didn't realize IFTI could do that :D

I agree! But how can you call something this beautiful evil? :)
 Thankfully, I have a nifty little Python script that saves me having to
 actually *type* all that out.  I give it a maximum number of arguments,
 and it goes off and generates the template for me.  Evil was never so 
 easy!

Hehehe, I use it as well for other sorts of stuff, where I have to generate lots of such code that would be handled by variadic templates if we had them ;)

I think you can manage this without needing to resort to autogenerated code by using a generic variadic template list generator. There will still be a hard-coded limit in the maximum number of function arguments though.
 How about taking it a step further and doing something like:
 
 ----
 import std.stdio;
 
 struct FuncMeta(int NumArgs, Ret, T0=void, T1=void /+, and, so, on+/) {
     alias FuncMeta Meta;
 
     static const int    numArgs = NumArgs;
     alias    Ret            RetType;
     alias    T0            Arg0Type;    // these might use static if as 
 well
     alias    T1            Arg1Type;
     /+
     ... and so on
     +/
 }

[snip the rest] Marvelous! this will be very useful for me in a number of places. /Oskar
May 31 2006
prev sibling parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Tom S wrote:
 
 How about taking it a step further and doing something like:
 
 ----
 import std.stdio;
 
 struct FuncMeta(int NumArgs, Ret, T0=void, T1=void /+, and, so, on+/) {
     alias FuncMeta Meta;
 
     static const int    numArgs = NumArgs;
     alias    Ret            RetType;
     alias    T0            Arg0Type;    // these might use static if as 
 well
     alias    T1            Arg1Type;
     /+
     ... and so on
     +/
 }
 
 
 template funcInfo(Ret) {
     FuncMeta!(0, Ret) funcInfo(Ret function() x) { assert(false); };
 }
 
 
 template funcInfo(Ret, T0) {
     FuncMeta!(1, Ret, T0) funcInfo(Ret function(T0) x) { assert(false); };
 }
 
 
 template funcInfo(Ret, T0, T1) {
     FuncMeta!(2, Ret, T0, T1) funcInfo(Ret function(T0, T1) x) { 
 assert(false); };
 }
 
 /+
 ....
 
 template funcInfo(Ret, T0, T1, ..., Tn) {
     ...
 }
 +/
 
 
 struct Foo {}
 void fooFunc(Foo a, float b) {}
 int barFunc(cfloat x) {}
 
 
 void main(char[][] args) {
     writefln("Number of args of main: ", funcInfo(&main).numArgs);
 
     writefln("\nfunc foo:");
     alias typeof(funcInfo(&fooFunc)) FooMeta;
     writefln(FooMeta.numArgs);
     writefln(typeid(FooMeta.RetType));
     writefln(typeid(FooMeta.Arg0Type));
     writefln(typeid(FooMeta.Arg1Type));
 
     writefln("\nfunc bar:");
     alias typeof(funcInfo(&barFunc)) BarMeta;
     writefln(BarMeta.numArgs);
     writefln(typeid(BarMeta.RetType));
     writefln(typeid(BarMeta.Arg0Type));
 
     static if (BarMeta.numArgs >= 2) {
         writefln(typeid(BarMeta.Arg1Type));
     } else {
         writefln("bar doesn't have Arg1Type");
     }
 }
 
 
 Note that the ArgXType could be static if'fed away so one would get an 
 error when trying to access them instead of having them alias to void.
 
 

Hum, pretty nice! -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 03 2006
prev sibling parent Bruno Medeiros <brunodomedeirosATgmail SPAM.com> writes:
Tom S wrote:
 Hey there :)
 
 I came up with this:
 
 ----
 
 import std.stdio;
 
 template Deref(T) {
         alias typeof(*T) Deref;
 }
 
 template RetType(T) {
         static if (is(Deref!(T) U == function)) {
                 alias U RetType;
         } else static assert (false);
 }
 
 template Bar(T, U=RetType!(T)) {
         U Bar(T t) {
                 writefln(typeid(U));
 
                 // do something :P
                 return U.init;
         }
 }
 
 
 cfloat func(int a, float b) {
         return 1.f + 0i;
 }
 
 
 void main() {
         writefln(Bar(&func));
 }
 
 
 -- 
 Tomasz Stachowiak  /+ a.k.a. h3r3tic +/

Just for the record, here's an alternative version, smaller, but less flexible(because you must know the parameters of the T function, and you have worse error messages): template Bar(T, U = typeof(T(0,0)) ) { U Bar(T t) { writefln(typeid(U)); return U.init; } } cfloat func(int a, float b) { return 1.f + 0i; } void main() { writefln(Bar(&func)); } -- Bruno Medeiros - CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jun 03 2006
prev sibling parent BCS <BCS pathlink.com> writes:
Kirk McDonald wrote:
[...]
 
 Simple enough. But I want to deduce the return type of this function 
 pointer, and make it the default value of a second template parameter.
 
 template Bar(T, U=<something>) {
     U Bar(T t) {
         return t();
     }
 }
 

I don't know if this is possible, however if it is and DMD stays true to form it will have a oddity, consider template Bar(T, U=<something>) { U Bar(T t) { return t(1.234567789); } float foo(float i) { return i; } double foo(double i) { return cast(float)i; } real foo(real i) { return cast(float)i; } Bar(foo); // which foo, which return type? as it stand the first foo encountered will be used (lexical order changes meaning in this case).
May 30 2006