www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Two things I thought would be simple

reply Shachar Shemesh <shachar weka.io> writes:
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?

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?
Mar 07 2018
next sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Mar 07 2018
parent reply Shachar Shemesh <shachar weka.io> writes:
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
Mar 07 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Mar 07 2018
parent Timon Gehr <timon.gehr gmx.ch> writes:
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
Mar 07 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
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 = &s; dg.funcptr = &v; // 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
Mar 07 2018
parent reply Shachar Shemesh <shachar weka.io> writes:
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.
Mar 08 2018
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Mar 08 2018