Jump to page: 1 2
Thread overview
Function pointer argument to function template
May 30, 2006
Kirk McDonald
May 30, 2006
Tom S
May 30, 2006
Kirk McDonald
May 31, 2006
Daniel Keep
May 31, 2006
Kirk McDonald
May 31, 2006
Tom S
May 31, 2006
Daniel Keep
May 31, 2006
Kirk McDonald
May 31, 2006
Tom S
May 31, 2006
Oskar Linde
Jun 03, 2006
Bruno Medeiros
Jun 03, 2006
Bruno Medeiros
May 30, 2006
BCS
May 30, 2006
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
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
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
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 31, 2006

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 31, 2006
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
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

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
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
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 +/
« First   ‹ Prev
1 2