Re: Two things I thought would be simple

2018-03-08 Thread Steven Schveighoffer via Digitalmars-d

On 3/8/18 3:29 AM, Shachar Shemesh wrote:

On 07/03/18 17:11, Steven Schveighoffer wrote:
Well, you could do it with templates, but obviously that is less 
desirable:


void func2(Args...)(Args args) if(is(typeof(func(args { return 
func(args); }


I never understood why anyone would use "is" for checking compilability 
when we have "__triats(compiles, func(args))".


There are some subtle differences in the past which caused me not to use 
__traits(compiles).


I don't remember what they are, maybe it's a bias that needs reexamining.

With that aside, I would urge you not to use this test. It makes the 
error case worse. Check this out:


void func1(int a) {
}

void func2(Args...)(Args args) if( __traits(compiles, func1(args))) {
    func1(args);
}

void func3(Args...)(Args args) {
    func1(args);
}

void main() {
     long var;

     func2(var);
     func3(var);
}

Which error would you rather get? Calling func2 results in:
test.d(15): Error: template test.func2 cannot deduce function from 
argument types !()(long), candidates are:
test.d(4):    test.func2(Args...)(Args args) if (__traits(compiles, 
func1(args)))


Calling func3 results in:
test.d(9): Error: function test.func1(int a) is not callable using 
argument types (long)
test.d(9):    cannot pass argument _param_0 of type long to 
parameter int a

test.d(16): Error: template instance `test.func3!long` error instantiating

When calling func3, the compiler is telling you what's wrong (i.e. - you 
are passing a long to a function expecting a var). When calling func2, 
all you know is that there is some problem.


Either way, you know the error is in calling func1. In most cases, the 
error is not so simple, and the template is not so simple, to the point 
that I think the first error will be more informative.


It also prevents a problem with template overloading.

With that out of the way, I don't like generating proxy functions with 
"Args..." templates. They generate unneeded code bloat:


void main() {
   int var1;
   ubyte var2;
   byte var3;
   ushort var4;
   short var5;
   uint var6;

   // This generates 6 instantiations of the same function
   func2(var1);
   func2(var2);
   func2(var3);
   func2(var4);
   func2(var5);
   func2(var6);
}

If I could use Params, there would be only one instantiation.


Yep, hence "obviously that is less desirable."

In an ideal world, there would be a way to remove all traces of func2. 
In other words, there would be a way to instruct the compiler to always 
inline func2, and not even include it in the resulting object file.


-Steve


Re: Two things I thought would be simple

2018-03-08 Thread Shachar Shemesh via Digitalmars-d

On 07/03/18 17:11, Steven Schveighoffer wrote:

Well, you could do it with templates, but obviously that is less desirable:

void func2(Args...)(Args args) if(is(typeof(func(args { return 
func(args); }


I never understood why anyone would use "is" for checking compilability 
when we have "__triats(compiles, func(args))".


With that aside, I would urge you not to use this test. It makes the 
error case worse. Check this out:


void func1(int a) {
}

void func2(Args...)(Args args) if( __traits(compiles, func1(args))) {
   func1(args);
}

void func3(Args...)(Args args) {
   func1(args);
}

void main() {
long var;

func2(var);
func3(var);
}

Which error would you rather get? Calling func2 results in:
test.d(15): Error: template test.func2 cannot deduce function from 
argument types !()(long), candidates are:
test.d(4):test.func2(Args...)(Args args) if (__traits(compiles, 
func1(args)))


Calling func3 results in:
test.d(9): Error: function test.func1(int a) is not callable using 
argument types (long)
test.d(9):cannot pass argument _param_0 of type long to 
parameter int a

test.d(16): Error: template instance `test.func3!long` error instantiating

When calling func3, the compiler is telling you what's wrong (i.e. - you 
are passing a long to a function expecting a var). When calling func2, 
all you know is that there is some problem.



With that out of the way, I don't like generating proxy functions with 
"Args..." templates. They generate unneeded code bloat:


void main() {
  int var1;
  ubyte var2;
  byte var3;
  ushort var4;
  short var5;
  uint var6;

  // This generates 6 instantiations of the same function
  func2(var1);
  func2(var2);
  func2(var3);
  func2(var4);
  func2(var5);
  func2(var6);
}

If I could use Params, there would be only one instantiation.


Re: Two things I thought would be simple

2018-03-07 Thread Timon Gehr via Digitalmars-d

On 07.03.2018 18:23, Jonathan M Davis wrote:

However, even doing something like

auto func2(Parameters!func args = AliasSeq!(1, 2, 3))
{
  return func(args);
}

doesn't work properly. If that version of the function is used, then the
call which passes all three arguments compiles, but the others don't, which
means that it's acting like the AliasSeq isn't even there (though it's
clearly doing_something_  with it, since you get a compilation error if one
of its values is void). I'd argue that either the values in the AliasSeq
should be being assigned, or they shouldn't be legal. Having them be there
but ignored just plain seems like a bug, but I have no idea what the
compiler is thinking here.


https://issues.dlang.org/show_bug.cgi?id=18572


Re: Two things I thought would be simple

2018-03-07 Thread Jonathan M Davis via Digitalmars-d
On Wednesday, March 07, 2018 18:59:51 Shachar Shemesh via Digitalmars-d 
wrote:
> On 07/03/18 16:42, Jonathan M Davis wrote:
> > AFAIK, there's no way to get the values of default arguments with type
> > inferrence
>
> I used that way right there in the code. ParameterDefaults!func return a
> tuple with the arguments. The problem is that "Tuple var = Tuple" does
> not work. Different errors for when all the original function's
> arguments have default and when some don't, but don't work either way.

Then clearly I read your code too quickly. Looking it over, there are
several problems here. First off, regardless of what's going on with the
default arguments, you made the functions return void instead of int or
auto, meaning that even

void func2(Parameters!func args)
{
 return func(args);
}

fails to compile. With that fixed, it works so long as you pass all of the
arguments. With

auto func2(Parameters!func args = ParameterDefaults!func)
{
 return func(args);
}

it doesn't work, because apparently, ParameterDefaults gives void if there
is no default argument, which makes sense, but makes what you're trying to
do not work. Either every parameter needs a default argument, or a wrapper
template that filled in the init value for the missing default argument
would be necessary. However, even doing something like

auto func2(Parameters!func args = AliasSeq!(1, 2, 3))
{
 return func(args);
}

doesn't work properly. If that version of the function is used, then the
call which passes all three arguments compiles, but the others don't, which
means that it's acting like the AliasSeq isn't even there (though it's
clearly doing _something_ with it, since you get a compilation error if one
of its values is void). I'd argue that either the values in the AliasSeq
should be being assigned, or they shouldn't be legal. Having them be there
but ignored just plain seems like a bug, but I have no idea what the
compiler is thinking here.

- Jonathan M Davis



Re: Two things I thought would be simple

2018-03-07 Thread Shachar Shemesh via Digitalmars-d

On 07/03/18 16:42, Jonathan M Davis wrote:

AFAIK, there's no way to get the values of default arguments with type
inferrence


I used that way right there in the code. ParameterDefaults!func return a 
tuple with the arguments. The problem is that "Tuple var = Tuple" does 
not work. Different errors for when all the original function's 
arguments have default and when some don't, but don't work either way.


Shachar


Re: Two things I thought would be simple

2018-03-07 Thread Steven Schveighoffer via Digitalmars-d

On 3/7/18 9:20 AM, Shachar Shemesh wrote:

import std.traits;
import std.stdio;

void func(int a, int b=2, int c=3) {
     return a+b+c;
}

void func2(Parameters!func args = ParameterDefaults!func) {
     return func(args);
}

void main() {
     writeln(func2(1));
     writeln(func2(1,1));
     writeln(func2(1,1,1));
}

So, this does not compile. I can do that with a mixin, but I would much 
rather not. Is there a non-Mixin method of copying not only the 
function's signature, but also its default values?


Well, you could do it with templates, but obviously that is less desirable:

void func2(Args...)(Args args) if(is(typeof(func(args { return 
func(args); }


The second thing I thought would be simple is to refer to call a 
struct's member.


struct S {
     void func(int a) {
     }
}

unittest {
     alias v = S.func;


This is actually a function that requires a `this` pointer. It can't be 
called unless you stuff it in a delegate:




     S s;
     s.v(12); // No property v for type S


void delegate(int) dg;
dg.ptr = 
dg.funcptr =  // note you need the address operator


     // UFCS to the rescue!
     v(s, 12);


dg(12);

It would be nice if v(s, 12) worked. Related DIP: 
https://github.com/dlang/DIPs/blob/master/DIPs/DIP1011.md


-Steve


Re: Two things I thought would be simple

2018-03-07 Thread Jonathan M Davis via Digitalmars-d
On Wednesday, March 07, 2018 16:20:19 Shachar Shemesh via Digitalmars-d 
wrote:
> import std.traits;
> import std.stdio;
>
> void func(int a, int b=2, int c=3) {
>  return a+b+c;
> }
>
> void func2(Parameters!func args = ParameterDefaults!func) {
>  return func(args);
> }
>
> void main() {
>  writeln(func2(1));
>  writeln(func2(1,1));
>  writeln(func2(1,1,1));
> }
>
> So, this does not compile. I can do that with a mixin, but I would much
> rather not. Is there a non-Mixin method of copying not only the
> function's signature, but also its default values?

AFAIK, there's no way to get the values of default arguments with type
inferrence, and without that, you pretty much need a way to know what the
arguments are on your own - either by using mixins or by using known enums
that can be reused both in the function arguments and wherever it is that
you want to reuse them. Having some way to get at default arguments via
__traits may be a perfectly reasonable enhancement request. I don't know
enough about how that would be implemented to know whether it would be a
problem or not though.

> The second thing I thought would be simple is to refer to call a
> struct's member.
>
> struct S {
>  void func(int a) {
>  }
> }
>
> unittest {
>  alias v = S.func;
>
>  S s;
>  s.v(12); // No property v for type S
>  // UFCS to the rescue!
>  v(s, 12);
>  // function S.func(int a) is not callable using argument types (S,
> int)
>
>  // Maybe using getMember?
>  __traits(getMember, S, "func")(s, 12); // Same error
>  // Maybe without UFCS
>  s.__traits(getMember, S, "func")(12);
>  // identifier expected following ., not __traits
> }
>
> Am I missing something?

No. As I understand it, you need a delegate to do something like this. I
don't recall the technical details as to why if I ever knew, but I
definitely agree that it's annoying - especially with getMember.

- Jonathan M Davis