Thread overview
overloads and parents. __traits confusion
Aug 11, 2014
John Colvin
Aug 12, 2014
Dicebot
Aug 12, 2014
John Colvin
Aug 12, 2014
anonymous
August 11, 2014
can someone talk me through the reasoning behind this:

import std.typetuple;

void foo(T)(T v){}
void foo(){}

version(ThisCompiles)
{
    alias Parent = TypeTuple!(__traits(parent, foo))[0];

    pragma(msg, __traits(getOverloads, Parent, "foo"));
// tuple()
}
else
{
    alias Parent = TypeTuple!(__traits(parent, foo!float))[0];

    pragma(msg, __traits(getOverloads, Parent, "foo"));
/*
/d54/f131.d(8): Error: foo (float v) is not callable using argument types () /d54/f131.d(8): Error: (__error).foo cannot be resolved
tuple()
*/
}
August 12, 2014
On Monday, 11 August 2014 at 13:00:27 UTC, John Colvin wrote:
>     alias Parent = TypeTuple!(__traits(parent, foo!float))[0];

Say hello to optional parens - you are trying to call foo!float() here and apply result to trait.
August 12, 2014
On Tuesday, 12 August 2014 at 05:23:53 UTC, Dicebot wrote:
> On Monday, 11 August 2014 at 13:00:27 UTC, John Colvin wrote:
>>    alias Parent = TypeTuple!(__traits(parent, foo!float))[0];
>
> Say hello to optional parens - you are trying to call foo!float() here and apply result to trait.

I thought so. Ugh.

This works though:


void foo(float v){}
void foo(int a){}

alias Parent = TypeTuple!(__traits(parent, foo))[0];

pragma(msg, __traits(getOverloads, Parent, "foo"));


so it seems that instantiated function templates in this case are called parenthesis-free, whereas normal functions are passed by name/alias/whatever.

All expressions that are used as compile-time args (whether in udas, traits or as template arguments) should require parenthesis to be called. Anything else is madness.

A!(foo1().foo2()) //pass result
A!(foo1.foo2())   //pass result
A!(foo1.foo2)     //pass function
A!(foo1)          //pass function
foo(foo1)         //pass result
August 12, 2014
On Monday, 11 August 2014 at 13:00:27 UTC, John Colvin wrote:
> can someone talk me through the reasoning behind this:
>
> import std.typetuple;
>
> void foo(T)(T v){}
> void foo(){}
>
> version(ThisCompiles)
> {
>     alias Parent = TypeTuple!(__traits(parent, foo))[0];
>
>     pragma(msg, __traits(getOverloads, Parent, "foo"));
> // tuple()
> }
> else
> {
>     alias Parent = TypeTuple!(__traits(parent, foo!float))[0];
>
>     pragma(msg, __traits(getOverloads, Parent, "foo"));
> /*
> /d54/f131.d(8): Error: foo (float v) is not callable using argument types () /d54/f131.d(8): Error: (__error).foo cannot be resolved
> tuple()
> */
> }

(I'm not sure if the following is completely correct, but it
makes a lot of sense to me at the moment.)

For clarity, the verbose version of that template:

template foo(T) /* the (eponymous) template */
{
     void foo(T v) {} /* the function; the eponymous member */
}

foo is the template:
   `foo.stringof` = "foo(T)(T v)".
Its parent is the module:
   `__traits(parent, foo).stringof` = "module test".
This is what you use in version(ThisCompiles). No problems.

foo!float is an instance of the template. Since the template is
eponymous, instantiations resolve to the eponymous member,
always. So foo!float is the function:
   `foo!float.mangleof.demangle` =
   "pure nothrow @nogc @safe void test7.foo!(float).foo(float)"
Its parent is the template instance, which resolves right back to
the function:
   `__traits(parent, foo!float).mangleof.demangle` =
   "pure nothrow @nogc @safe void test7.foo!(float).foo(float)"
   (just foo!float again)

Apparently, you cannot get out of an eponymous template
instantiation with __traits(parent, ...). Maybe __traits(parent,
...) of an eponymous template instance should be the parent of
the template. The current behaviour seems useless to me.

Simple test case without functions (no additional trouble from
optional parentheses):

struct S(T) {}
/* All print "S!int": */
pragma(msg, S!int);
pragma(msg, __traits(parent, S!int));
pragma(msg, __traits(parent, __traits(parent, S!int)));