Thread overview
template parameter inference and introspection
Feb 23, 2017
John Colvin
Feb 23, 2017
Meta
Feb 23, 2017
Meta
Feb 24, 2017
John Colvin
Feb 24, 2017
Meta
Feb 24, 2017
John Colvin
February 23, 2017
Is there any way to get a reference/alias to the instantiation of a template function that would be called, given certain parameters? I.e. to get the result of whatever template parameter inference (and overload resolution) has occurred?

E.g. for some arbitrarily complex foo:

static assert(__traits(compiles, foo(3)));
alias fooWithInt = someMagic(foo(3));

so if foo was `void foo(T)(T t) {}` then `fooWithInt` would be `foo!int`, but if it was `void foo(Q = float, T = long)(T t)` then `fooWithInt` would be `foo!(float, int)`
February 23, 2017
On Thursday, 23 February 2017 at 16:01:44 UTC, John Colvin wrote:
> Is there any way to get a reference/alias to the instantiation of a template function that would be called, given certain parameters? I.e. to get the result of whatever template parameter inference (and overload resolution) has occurred?
>
> E.g. for some arbitrarily complex foo:
>
> static assert(__traits(compiles, foo(3)));
> alias fooWithInt = someMagic(foo(3));
>
> so if foo was `void foo(T)(T t) {}` then `fooWithInt` would be `foo!int`, but if it was `void foo(Q = float, T = long)(T t)` then `fooWithInt` would be `foo!(float, int)`

I don't believe so, because foo(3) is a value (void), not a type like foo!int would be. You can't get it back after you've called the function. You would have to do something like:

alias fooWithInt = someMagic!foo(3);

Where someMagic constructs the alias to foo!int based on the type of arguments passed, or something like that.
February 23, 2017
On Thursday, 23 February 2017 at 18:21:51 UTC, Meta wrote:
> On Thursday, 23 February 2017 at 16:01:44 UTC, John Colvin wrote:
>> Is there any way to get a reference/alias to the instantiation of a template function that would be called, given certain parameters? I.e. to get the result of whatever template parameter inference (and overload resolution) has occurred?
>>
>> E.g. for some arbitrarily complex foo:
>>
>> static assert(__traits(compiles, foo(3)));
>> alias fooWithInt = someMagic(foo(3));
>>
>> so if foo was `void foo(T)(T t) {}` then `fooWithInt` would be `foo!int`, but if it was `void foo(Q = float, T = long)(T t)` then `fooWithInt` would be `foo!(float, int)`
>
> I don't believe so, because foo(3) is a value (void), not a type like foo!int would be. You can't get it back after you've called the function. You would have to do something like:
>
> alias fooWithInt = someMagic!foo(3);
>
> Where someMagic constructs the alias to foo!int based on the type of arguments passed, or something like that.

A quick and rough example I threw together. Annoyingly, I can't figure out a way to tell the compiler that I want to printout the symbol of fooWithInt, not call it without parens. The best I can do is print out its type.

void foo(T)(T t) {}
void foo(Q = float, T = long)(T t) {}

alias Typeof(alias v) = typeof(v);

template getInstantiation(alias f, T...)
{
    import std.meta;

    alias getInstantiation = f!(staticMap!(Typeof, T));
}

alias fooWithInt = getInstantiation!(foo, 3);
alias fooWithLong = getInstantiation!(foo, 3L);

void main()
{
    pragma(msg, typeof(fooWithInt));
    pragma(msg, typeof(fooWithLong));
}
February 24, 2017
On Thursday, 23 February 2017 at 18:33:33 UTC, Meta wrote:
> On Thursday, 23 February 2017 at 18:21:51 UTC, Meta wrote:
>> On Thursday, 23 February 2017 at 16:01:44 UTC, John Colvin wrote:
>>> Is there any way to get a reference/alias to the instantiation of a template function that would be called, given certain parameters? I.e. to get the result of whatever template parameter inference (and overload resolution) has occurred?
>>>
>>> E.g. for some arbitrarily complex foo:
>>>
>>> static assert(__traits(compiles, foo(3)));
>>> alias fooWithInt = someMagic(foo(3));
>>>
>>> so if foo was `void foo(T)(T t) {}` then `fooWithInt` would be `foo!int`, but if it was `void foo(Q = float, T = long)(T t)` then `fooWithInt` would be `foo!(float, int)`
>>
>> I don't believe so, because foo(3) is a value (void), not a type like foo!int would be. You can't get it back after you've called the function. You would have to do something like:
>>
>> alias fooWithInt = someMagic!foo(3);
>>
>> Where someMagic constructs the alias to foo!int based on the type of arguments passed, or something like that.
>
> A quick and rough example I threw together. Annoyingly, I can't figure out a way to tell the compiler that I want to printout the symbol of fooWithInt, not call it without parens. The best I can do is print out its type.
>
> void foo(T)(T t) {}
> void foo(Q = float, T = long)(T t) {}
>
> alias Typeof(alias v) = typeof(v);
>
> template getInstantiation(alias f, T...)
> {
>     import std.meta;
>
>     alias getInstantiation = f!(staticMap!(Typeof, T));
> }
>
> alias fooWithInt = getInstantiation!(foo, 3);
> alias fooWithLong = getInstantiation!(foo, 3L);
>
> void main()
> {
>     pragma(msg, typeof(fooWithInt));
>     pragma(msg, typeof(fooWithLong));
> }

Unfortunately that only works by accident of my example. A counterexample:

T foo(Q = float, T = short)(T t) { return t; }

alias Typeof(alias v) = typeof(v);

template getInstantiation(alias f, T...)
{
    import std.meta;
	
    alias getInstantiation = f!(staticMap!(Typeof, T));
}

static assert(is(typeof(foo(3)) == int)); // ok
static assert(is(typeof(getInstantiation!(foo, 3)(3)) == int)); // fails

February 24, 2017
On Friday, 24 February 2017 at 11:17:46 UTC, John Colvin wrote:
> Unfortunately that only works by accident of my example. A counterexample:
>
> T foo(Q = float, T = short)(T t) { return t; }
>
> alias Typeof(alias v) = typeof(v);
>
> template getInstantiation(alias f, T...)
> {
>     import std.meta;
> 	
>     alias getInstantiation = f!(staticMap!(Typeof, T));
> }
>
> static assert(is(typeof(foo(3)) == int)); // ok
> static assert(is(typeof(getInstantiation!(foo, 3)(3)) == int)); // fails

This looks like VRP is kicking in when it shouldn't, or maybe it's a different bug. 3 should be typed as int by default unless we explicitly ask for something else.
February 24, 2017
On Friday, 24 February 2017 at 14:06:22 UTC, Meta wrote:
> On Friday, 24 February 2017 at 11:17:46 UTC, John Colvin wrote:
>> Unfortunately that only works by accident of my example. A counterexample:
>>
>> T foo(Q = float, T = short)(T t) { return t; }
>>
>> alias Typeof(alias v) = typeof(v);
>>
>> template getInstantiation(alias f, T...)
>> {
>>     import std.meta;
>> 	
>>     alias getInstantiation = f!(staticMap!(Typeof, T));
>> }
>>
>> static assert(is(typeof(foo(3)) == int)); // ok
>> static assert(is(typeof(getInstantiation!(foo, 3)(3)) == int)); // fails
>
> This looks like VRP is kicking in when it shouldn't, or maybe it's a different bug. 3 should be typed as int by default unless we explicitly ask for something else.

VRP propagation is what makes the call possible, but that's a distraction. The problem is that getInstantiation is setting Q to int and leaving T to be it's default type of short, which is not the same as if you just call with an int (which infers T from t and leaves Q as it's default float. Fundamentally, you can't assume the same order of runtime and template arguments.