www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - template parameter inference and introspection

reply John Colvin <john.loughran.colvin gmail.com> writes:
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)`
Feb 23
parent reply Meta <jared771 gmail.com> writes:
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.
Feb 23
parent reply Meta <jared771 gmail.com> writes:
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)); }
Feb 23
parent reply John Colvin <john.loughran.colvin gmail.com> writes:
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
Feb 24
parent reply Meta <jared771 gmail.com> writes:
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.
Feb 24
parent John Colvin <john.loughran.colvin gmail.com> writes:
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.
Feb 24