www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template parameter deduction for constructors?

reply "bearophile" <bearophileHUGS lycos.com> writes:
This is one of the few C++14 core language proposals that seem 
interesting for D:

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

The purpose of this idea is to avoid the helper functions that 
are currently used in D to build template structs/classes. Even 
if this feature is restricted to only a subset of all the 
templated structs/classes it seems useful to avoid some 
boilerplate code.

Is this idea adaptable to D?

Bye,
bearophile
Apr 12 2013
next sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 04/12/2013 04:00 PM, bearophile wrote:

 This is one of the few C++14 core language proposals that seem
 interesting for D:

 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

 The purpose of this idea is to avoid the helper functions that are
 currently used in D to build template structs/classes. Even if this
 feature is restricted to only a subset of all the templated
 structs/classes it seems useful to avoid some boilerplate code.

 Is this idea adaptable to D?

 Bye,
 bearophile

This has come up multiple times before, most recently on the D.learn forum. My reaction is that what if the constructor is also a template? Or what if only the constructor is a template? To paraphrase Steven Schveighoffer: - if only the type is a template then the constructor parameters are used for deducing the template parameters of the type - if only the constructor is a template, then the constructor parameters are used for deducing the template parameters of the constructor - if both are templates then it is an ambiguity I still think that the code will be confusing. If the proposal is implemented, the two lines in main below will look the same but will have different meanings: import std.stdio; // Today: struct S { this(T)(T param) { writefln("constructing with %s", T.stringof); } } // Proposed: struct S2(T) { this(T param) { writefln("constructing S2!%s", T.stringof); } } void main() { /* Can we tell by reading the following code whether S or S2 is a * template, or whether the constructor of one of them is a * template? Do we care? */ auto s = S(42); auto s2 = S2(43); // This is proposed; not valid yet. } Note that the C++ proposal does not touch on this point. Ali
Apr 12 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Friday, 12 April 2013 at 23:00:20 UTC, bearophile wrote:
 The purpose of this idea is to avoid the helper functions that 
 are currently used in D to build template structs/classes. Even 
 if this feature is restricted to only a subset of all the 
 templated structs/classes it seems useful to avoid some 
 boilerplate code.

This just seems like a bandaid solution to the problem. If it works in this case then beginners will think it works in every case, and will be even more confused when it stops working. Simple with minor inconvenience is better than complex in my opinion.
Apr 13 2013
prev sibling next sibling parent "David Nadlinger" <see klickverbot.at> writes:
On Saturday, 13 April 2013 at 08:40:15 UTC, Peter Alexander wrote:
 This just seems like a bandaid solution to the problem. If it 
 works in this case then beginners will think it works in every 
 case, and will be even more confused when it stops working.

When would it stop working? You might want to add any such example to http://d.puremagic.com/issues/show_bug.cgi?id=6082.
 Simple with minor inconvenience is better than complex in my 
 opinion.

The complexity is already there, in the form of (free) ITFI functions. David
Apr 13 2013
prev sibling next sibling parent "Peter Alexander" <peter.alexander.au gmail.com> writes:
On Saturday, 13 April 2013 at 15:52:01 UTC, David Nadlinger wrote:
 On Saturday, 13 April 2013 at 08:40:15 UTC, Peter Alexander 
 wrote:
 This just seems like a bandaid solution to the problem. If it 
 works in this case then beginners will think it works in every 
 case, and will be even more confused when it stops working.

When would it stop working? You might want to add any such example to http://d.puremagic.com/issues/show_bug.cgi?id=6082.

Something like this: struct Foo(T) { static if (is(T == int)) this(string x) {} else this(T x) {} } T is ambiguous for Foo("bar") Also this: struct Foo(T) { this(T x) {} this(int x) {} } Calls to Foo(x) are ambiguous when is(typeof(x) : int). Allowing deduction in this case would be frustrating. Imagine having a struct where this worked, and then you wanted to add a new constructor, or maybe just modify the constructor for one instantiation. You would then have to change all calls to explicitly specify parameters.
 The complexity is already there, in the form of (free) ITFI 
 functions.

Unfortunately you are right because of the eponymous template hack. Without it, normal functions are non-complex. void foo(T)(T x) {} void foo(string x) {} Here, foo("bar") is unambiguously the second call. (D compilers don't allow template/non-template overloads at the moment due to a bug, but this should work). Unfortunately the eponymous hack falls down quite easily. For example, IFTI on this function fails: template foo(T) { static if (true) void foo(T)(T x) {} } So it is just as fragile as this proposal.
Apr 14 2013
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 04/13/2013 01:00 AM, bearophile wrote:
 This is one of the few C++14 core language proposals that seem
 interesting for D:

 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

 The purpose of this idea is to avoid the helper functions that are
 currently used in D to build template structs/classes. Even if this
 feature is restricted to only a subset of all the templated
 structs/classes it seems useful to avoid some boilerplate code.

 Is this idea adaptable to D?

 Bye,
 bearophile

I think the differences to be accounted for are basically that D has n-ary default constructors, static opCall, and static if. Furthermore, with IFTI, it is possible to only specify the prefix of the template arguments. --- struct S(T...){ T data; } S(1,2,3); // ? S!double(1,2,3); // ? --- struct S(T...){ T data; static if(T.length){ this(T x){ } } } S(1,2,3); // ? --- struct S(T...){ static opCall(T)(T args){ } } S(1,2,3); // ? ---
Apr 14 2013
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 04/15/2013 05:18 PM, Steven Schveighoffer wrote:
 On Sun, 14 Apr 2013 11:47:47 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 04/13/2013 01:00 AM, bearophile wrote:
 This is one of the few C++14 core language proposals that seem
 interesting for D:

 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

 The purpose of this idea is to avoid the helper functions that are
 currently used in D to build template structs/classes. Even if this
 feature is restricted to only a subset of all the templated
 structs/classes it seems useful to avoid some boilerplate code.

 Is this idea adaptable to D?

 Bye,
 bearophile

I think the differences to be accounted for are basically that D has n-ary default constructors, static opCall, and static if. Furthermore, with IFTI, it is possible to only specify the prefix of the template arguments. --- struct S(T...){ T data; } S(1,2,3); // ?

Should work. The implicit default constructor should be treated as a function.

It cannot be treated exactly like a function because it depends on the struct layout. It is very well possible that the struct layout cannot be easily determined without instantiation. The example below demonstrates that it is not entirely obvious.
...

 ---

 struct S(T...){
      T data;
      static if(T.length){
          this(T x){ }
      }
 }

 S(1,2,3); // ?

I think this should follow IFTI rules. If this is allowed: template foo(T...) { static if(T.length) void foo(T x) {} }

This probably shouldn't be allowed because a compiler cannot satisfy arbitrary expressions.
 Then the struct should be allowed as well.

It is more subtle than that. The layout of S begins with T data. Does this mean S(1,2,3) instantiates to S!(int,int,int)(1,2,3), as above ? When does this happen? Is it checked that the constructor used for guessing the parameters is the constructor that gets called? Also: What happens if there is more than one constructor?
 ...

Apr 15 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 12 Apr 2013 20:00:58 -0400, Ali =C3=87ehreli <acehreli yahoo.com=
 wrote:

 On 04/12/2013 04:00 PM, bearophile wrote:

  > This is one of the few C++14 core language proposals that seem
  > interesting for D:
  >
  > http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html
  >
  > The purpose of this idea is to avoid the helper functions that are
  > currently used in D to build template structs/classes. Even if this=

  > feature is restricted to only a subset of all the templated
  > structs/classes it seems useful to avoid some boilerplate code.
  >
  > Is this idea adaptable to D?
  >
  > Bye,
  > bearophile

 This has come up multiple times before, most recently on the D.learn  =

 forum. My reaction is that what if the constructor is also a template?=

 Or what if only the constructor is a template?

 To paraphrase Steven Schveighoffer:

 - if only the type is a template then the constructor parameters are  =

 used for deducing the template parameters of the type

 - if only the constructor is a template, then the constructor paramete=

 are used for deducing the template parameters of the constructor

 - if both are templates then it is an ambiguity

In other words, if both are templates, it requires what is done today = (specifically instantiating the class/struct template, then you can use = = IFTI on the constructor). This is not terrible.
 I still think that the code will be confusing. If the proposal is  =

 implemented, the two lines in main below will look the same but will  =

 have different meanings:

 import std.stdio;

 // Today:
 struct S
 {
      this(T)(T param)
      {
          writefln("constructing with %s", T.stringof);
      }
 }

 // Proposed:
 struct S2(T)
 {
      this(T param)
      {
          writefln("constructing S2!%s", T.stringof);
      }
 }

 void main()
 {
      /* Can we tell by reading the following code whether S or S2 is a=

       * template, or whether the constructor of one of them is a
       * template? Do we care? */
      auto s =3D S(42);
      auto s2 =3D S2(43); // This is proposed; not valid yet.
 }

 Note that the C++ proposal does not touch on this point.

I don't think this is confusing any more than standard IFTI or automatic= = type deduction is confusing. -Steve
Apr 15 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 14 Apr 2013 10:29:11 -0400, Peter Alexander  
<peter.alexander.au gmail.com> wrote:

 On Saturday, 13 April 2013 at 15:52:01 UTC, David Nadlinger wrote:
 On Saturday, 13 April 2013 at 08:40:15 UTC, Peter Alexander wrote:
 This just seems like a bandaid solution to the problem. If it works in  
 this case then beginners will think it works in every case, and will  
 be even more confused when it stops working.

When would it stop working? You might want to add any such example to http://d.puremagic.com/issues/show_bug.cgi?id=6082.

Something like this: struct Foo(T) { static if (is(T == int)) this(string x) {} else this(T x) {} } T is ambiguous for Foo("bar")

template Foo(T) { static if(is(T == int)) Foo(string x) {} else Foo(T x) {} } This is the equivalent function. It correctly requires explicit instantiation, and cannot involve IFTI.
 Also this:

 struct Foo(T)
 {
      this(T x) {}
      this(int x) {}
 }

 Calls to Foo(x) are ambiguous when is(typeof(x) : int).

Foo!int is not ambiguous, it's illegal. Technically, a valid implementation would be: struct Foo(T) { this(T x) {} static if(is(T != int)) this(int x) {} } Which I think should instantiate just fine, but I don't know.
 Allowing deduction in this case would be frustrating. Imagine having a  
 struct where this worked, and then you wanted to add a new constructor,  
 or maybe just modify the constructor for one instantiation. You would  
 then have to change all calls to explicitly specify parameters.

This is due to issues with the compiler. It currently requires a specific form to use IFTI, this would necessarily need to change. If the constructor is ambiguous or did not involve the template type (such as your int x argument above), then you would have to explicitly instantiate, necessarily. But you should be allowed to do something like this: struct S(T) { this(T t) {} this(T t, string s) {} }
 The complexity is already there, in the form of (free) ITFI functions.

Unfortunately you are right because of the eponymous template hack. Without it, normal functions are non-complex. void foo(T)(T x) {} void foo(string x) {} Here, foo("bar") is unambiguously the second call. (D compilers don't allow template/non-template overloads at the moment due to a bug, but this should work).

This is not the same as a template struct with multiple constructors. It is like having two structs, one which is templated, one which is not. Given that the above currently doesn't work, I don't know what should happen with multiple structs, but I think it should be consistent with correct overloaded function behavior.
 Unfortunately the eponymous hack falls down quite easily. For example,  
 IFTI on this function fails:

 template foo(T)
 {
      static if (true)
          void foo(T)(T x) {}
 }

 So it is just as fragile as this proposal.

That is an issue with IFTI, not the proposal. Any enhancements with IFTI should translate to this as well. I think your concerns are a bit exaggerated. This is something that is more than annoying for the cases where there is one constructor, and by default falls back on current behavior. We are adding to the current behavior, not breaking it, any perceived incorrect behavior is not any more confusing that the current IFTI rules. -Steve
Apr 15 2013
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Sun, 14 Apr 2013 11:47:47 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 04/13/2013 01:00 AM, bearophile wrote:
 This is one of the few C++14 core language proposals that seem
 interesting for D:

 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

 The purpose of this idea is to avoid the helper functions that are
 currently used in D to build template structs/classes. Even if this
 feature is restricted to only a subset of all the templated
 structs/classes it seems useful to avoid some boilerplate code.

 Is this idea adaptable to D?

 Bye,
 bearophile

I think the differences to be accounted for are basically that D has n-ary default constructors, static opCall, and static if. Furthermore, with IFTI, it is possible to only specify the prefix of the template arguments. --- struct S(T...){ T data; } S(1,2,3); // ?

Should work. The implicit default constructor should be treated as a function.
 S!double(1,2,3); // ?

Should work with T == {double, int, int}
 ---

 struct S(T...){
      T data;
      static if(T.length){
          this(T x){ }
      }
 }

 S(1,2,3); // ?

I think this should follow IFTI rules. If this is allowed: template foo(T...) { static if(T.length) void foo(T x) {} } Then the struct should be allowed as well.
 ---

 struct S(T...){
      static opCall(T)(T args){ }
 }

 S(1,2,3); // ?

I would say it should work. -Steve
Apr 15 2013
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 15 Apr 2013 13:21:52 -0400, Timon Gehr <timon.gehr gmx.ch> wrote:

 On 04/15/2013 05:18 PM, Steven Schveighoffer wrote:
 On Sun, 14 Apr 2013 11:47:47 -0400, Timon Gehr <timon.gehr gmx.ch>  
 wrote:

 On 04/13/2013 01:00 AM, bearophile wrote:
 This is one of the few C++14 core language proposals that seem
 interesting for D:

 http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3602.html

 The purpose of this idea is to avoid the helper functions that are
 currently used in D to build template structs/classes. Even if this
 feature is restricted to only a subset of all the templated
 structs/classes it seems useful to avoid some boilerplate code.

 Is this idea adaptable to D?

 Bye,
 bearophile

I think the differences to be accounted for are basically that D has n-ary default constructors, static opCall, and static if. Furthermore, with IFTI, it is possible to only specify the prefix of the template arguments. --- struct S(T...){ T data; } S(1,2,3); // ?

Should work. The implicit default constructor should be treated as a function.

It cannot be treated exactly like a function because it depends on the struct layout. It is very well possible that the struct layout cannot be easily determined without instantiation. The example below demonstrates that it is not entirely obvious.

I am not very well-versed in exactly how the compiler is implemented, but to me, the above is not much different than: struct S(T...){ T data; } S(T...) makeS(T...)(T data) { return S!T(data);} If the compiler can examine makeS enough to figure out how to instantiate makeS, then why can't it figure out how to instantiate S directly?
 ...

 ---

 struct S(T...){
      T data;
      static if(T.length){
          this(T x){ }
      }
 }

 S(1,2,3); // ?

I think this should follow IFTI rules. If this is allowed: template foo(T...) { static if(T.length) void foo(T x) {} }

This probably shouldn't be allowed because a compiler cannot satisfy arbitrary expressions.

Then if that cannot be allowed, the struct cannot be allowed.
 Then the struct should be allowed as well.

It is more subtle than that. The layout of S begins with T data. Does this mean S(1,2,3) instantiates to S!(int,int,int)(1,2,3), as above ? When does this happen? Is it checked that the constructor used for guessing the parameters is the constructor that gets called?

I predicated this statement on the condition that IFTI allows it. If that isn't true, then it makes no sense for structs to be able to allow it. I'm going on the principal that IFTI allows "external" constructors of the same form. I view it as a kind of lowering. But it only works if the compiler can figure out the struct layout without binding the exact types. Basically, it must be able to examine the constructor and/or variable layout in order to imply the appropriate constructor function parameters. I also realize that this complicates IFTI, since it currently requires that nothing but the function declaration be inside the template. IFTI will have to be more complex in order for this to work.
 Also: What happens if there is more than one constructor?

More than one constructor can work, it depends on how the constructor is specified. For example: struct S(T) { this(T t) {} this(T t, string s) {} } seems fine to me. Again, it requires upgrades to IFTI. -Steve
Apr 15 2013