www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Question about alias and getOverloads

reply uranuz <neuranuz gmail.com> writes:
Hello! I have a question about `alias` template parameter and 
getOverloads.
For instance I have some code like this:
// -----
import std;
import core.thread;

void foo(string param1) {}
void foo(string param1, int param2) {}

template Bar(alias Func)
{
     // Next line is not valid now. This is my understanding of 
how I wish it would work...
     pragma(msg, __traits(getOverloads, Func));
}

void main()
{
     Bar!foo;
}
//---
By the spec:
https://dlang.org/spec/traits.html#getOverloads
The signature of `getOverloads` trait requires me to pass a 
symbol where my function is contained (it could be class, struct 
or module). And a name of function that I want to get overloads 
for.

Let's imagine that `foo` overloads and Bar template are in 
different modules. And `Bar` template doesn't have no direct 
access to module where `foo` is contained. And `Bar` is 
instantiated outside module where `Bar` is declared.

I have two questions:
1. Whether `Bar` instantiation would get first, second or both 
overloads of function? Or maybe I would get two instantiations of 
template by the number of overloads? I don't understand well how 
this mechanic is working. Is it documented somewhere? Does 
actually one alias parameter represents only one symbol from 
overload set or all of them?

2. If alias parameter represents multiple symbols from overload 
set. Then I wish I could access to list of overloads having alias 
parameter. I wish to get count of overloads in my real case in 
order to issue an error that overloaded are not supported by my 
code.
Jan 28 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 28 January 2020 at 19:26:03 UTC, uranuz wrote:
 Hello! I have a question about `alias` template parameter and 
 getOverloads.
 For instance I have some code like this:
 // -----
 import std;
 import core.thread;

 void foo(string param1) {}
 void foo(string param1, int param2) {}

 template Bar(alias Func)
 {
     // Next line is not valid now. This is my understanding of 
 how I wish it would work...
     pragma(msg, __traits(getOverloads, Func));
 }
__traits(getOverloads, __traits(parent, Func), __traits(identifier, Func))
Jan 28 2020
parent reply uranuz <neuranuz gmail.com> writes:
Thanks for advice ;) This looks like some `standard trick` that 
is yet not learnt or forgoten by me personally. The key was in 
using `parent` trait. This is what I failed to think of. This is 
working as expected:
//-----
import std;
import core.thread;
import std.meta: AliasSeq;

void foo(string param1) {}
void foo(string param1, int param2, bool param3) {}

template Bar(alias Func)
{
     alias Bar = __traits(getOverloads, __traits(parent, Func), 
__traits(identifier, Func));
}

void main()
{
	import std.traits: fullyQualifiedName, Parameters;
     import std.conv: text;
     static foreach( It; Bar!foo ) {
     	pragma(msg, fullyQualifiedName!It ~ "/" ~ 
(Parameters!It).length.text);
     }
}
//-----
Output:
onlineapp.foo/1
onlineapp.foo/3
//-----

So as far as I understand alias template parameter gives access 
to all of items of overload set as I was expecting. The only 
problem is that I didn't found something about it in 
specification about templates:
https://dlang.org/spec/template.html
Jan 28 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Tuesday, 28 January 2020 at 19:59:15 UTC, uranuz wrote:
 So as far as I understand alias template parameter gives access 
 to all of items of overload set as I was expecting. The only 
 problem is that I didn't found something about it in 
 specification about templates:
 https://dlang.org/spec/template.html
It's documented under "Alias Declaration":
 Aliases can also import a set of overloaded functions, that can 
 be overloaded with functions in the current scope:
https://dlang.org/spec/declaration.html#alias
Jan 28 2020
parent reply uranuz <neuranuz gmail.com> writes:
I have read it two or three times just before writing my question:
https://dlang.org/spec/declaration.html#alias
And also a have read all the dlang docs several time few years 
ago... ;)
But I don't see what do you you mean by writing that it was 
menioned here. I don't se any words or any examples that describe 
how `alias` works with function overload sets. Is it a blind 
assumption that it was written here?

The only example that someone can mix up and consider as answer 
to my question if actually didn't read it with attention. It is 
example bellow:
```
alias myint = int;

void foo(int x) { ... }
void foo(myint m) { ... } // error, multiply defined function foo
```
But the only thing that this example illustrates is that `myint` 
and `int` is actualy the same  type. So this overload set is just 
incorrect. I don't ask you to consider the case where overload 
set is incorrect. So this didn't answered my question in any way.

Looks like this is some kind of nasty details that language 
developers don't "proud of" and don't like to discover details 
about how it's working. Or just lazy enough to do so ;)

Still I figured answer myself using your example. The answer is 
not full, but yet enough for me for practical usage. But the lack 
of description about it makes me think that I am not the last man 
who will have his curiosity not fully satisfied about this aspect 
of language.

And also __traits(parent, Func) seems like not universal for all 
cases with functions, because it fails on lambda-functions. For 
instance:
//----
import std;

template Bar(alias Func) {
     alias Bar = __traits(getOverloads, __traits(parent, Func), 
__traits(identifier, Func));
}

void main()
{
     alias Test = Bar!(() { return `test`; });
}
//----
Output:
onlineapp.d(4): Error: no property __lambda1 for type void
onlineapp.d(4): Error: main().__lambda1 cannot be resolved
onlineapp.d(9): Error: template instance onlineapp.Bar!(function 
() pure nothrow  nogc  safe => "test") error instantiating
//----
Seems that it fails to get parent for lambda. I don't know why? 
What parent should be for labmda?

Thanks for attention!
Jan 28 2020
parent reply uranuz <neuranuz gmail.com> writes:
I apologise that I need to revive this discussion again. But 
still I got answer to only one of my questions. I know that it is 
a common practice in programmers society that when someone asks 
more than 1 question. Then people who answer them usually choose 
only one of these questions that is the most easiest to answer. 
But the rest of the questions are just ignored. But the topic is 
still considered resolved. Person who answered `increased his 
carma`. All are happy. Except for the man who asked the question, 
but only got answer that is trivial, easy-to-answer or 
easy-to-find by himself. And actually got the answer to only one 
of the questions. And it is the less important.

At my job in this case I usually ask questions only one by one. 
It gives to person that shall try to answer no options and to 
answer only to this question. So only when I got answer that 
satisfies me I start to ask the next question.
I didn't want to use this technique there, because I believe that 
D community is better...

Sorry for this disgression, but I still don't understand several 
main points:
1. Why API for __traits(getOverloads) is so strange. Why I need 
to find parent for symbol myself and pass actual symbol name as 
string, but not actually pass a symbol itself? It is very strange 
to me...
2. Where in the documentation is mentioned that when I create 
alias to name that is a function or method that has overloads 
then this alias is actually an alias not only for the first or 
second or randomly selected overload in overload set, but an 
alias for all of overloads in set at the same time?

Thanks. Have a good day!
Jan 30 2020
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 30, 2020 at 06:20:47PM +0000, uranuz via Digitalmars-d-learn wrote:
[...]
 1. Why API for __traits(getOverloads) is so strange. Why I need to
 find parent for symbol myself and pass actual symbol name as string,
 but not actually pass a symbol itself? It is very strange to me...
Actually, __traits was not intended to be used by end user code. It's supposed to be a raw, low-level interface into compiler internals, that the standard library uses to implement a nicer user-facing API, i.e., std.traits. Unfortunately, std.traits hasn't kept up with the proliferation of __traits, and now we have pretty widespread use of __traits in user code for doing things std.traits can't do, or can't do very well. If you think __traits(getOverloads) is strange, wait till you see the weird function parameter tuples that behave like tuples but actually have extra information associated with them, but this information "disappears" when you index the tuple or otherwise manipulate it like a "normal" tuple. Instead, to extract this information you must take 1-element slices of it. Thankfully, this ugly mess is hidden behind nicely-clothed citizens of std.traits, so user code rarely has to deal with this craziness. Arguably, any weird behaviours of __traits ought to be reason to file a bug to add a nicer-behaving API to std.traits. In fact, any __traits that doesn't have a std.traits analogue arguably should be a bug.
 2. Where in the documentation is mentioned that when I create alias to
 name that is a function or method that has overloads then this alias
 is actually an alias not only for the first or second or randomly
 selected overload in overload set, but an alias for all of overloads
 in set at the same time?
[...] It's probably missing/incomplete documentation. File a bug. T -- Change is inevitable, except from a vending machine.
Jan 30 2020
parent uranuz <neuranuz gmail.com> writes:
OK. Thanks. Created two reports related to these questions:
https://issues.dlang.org/show_bug.cgi?id=20553
https://issues.dlang.org/show_bug.cgi?id=20555
Feb 01 2020
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 30 January 2020 at 18:20:47 UTC, uranuz wrote:
 2. Where in the documentation is mentioned that when I create 
 alias to name that is a function or method that has overloads 
 then this alias is actually an alias not only for the first or 
 second or randomly selected overload in overload set, but an 
 alias for all of overloads in set at the same time?

 Thanks. Have a good day!
It is directly implied (although not outright stated) by the paragraph I quoted in my previous reply, which says that aliases may be used to merge overload sets. This feature would not work if the alias referred only to a single member of the overload set. The documentation here could definitely use some improvement, but I think it is at least sufficient to reassure you that the behavior is intentional and can be safely relied upon.
Jan 30 2020