Jump to page: 1 2
Thread overview
Template argument deduction and default args
Jul 24, 2014
Manu
Jul 24, 2014
anonymous
Jul 24, 2014
Manu
Jul 24, 2014
anonymous
Jul 24, 2014
Daniel Murphy
Jul 24, 2014
Manu
Jul 24, 2014
Jakob Ovrum
Jul 24, 2014
John Colvin
Jul 24, 2014
Manu
Jul 24, 2014
John Colvin
Jul 24, 2014
Manu
Jul 24, 2014
Manu
Jul 24, 2014
Manu
Jul 24, 2014
John Colvin
Jul 24, 2014
Manu
Jul 24, 2014
Daniel Murphy
Jul 24, 2014
Manu
Jul 25, 2014
Jacob Carlborg
Jul 24, 2014
w0rp
July 24, 2014
I'm running into consistent problems with default args and argument
deduction in templates.
There seem to be 2 consistent classes of problem:

struct S(size_t len = 10)
{
  ubyte[len] data;
}

S!100 x; // this works fine
S y; // this doesn't work (!)
S!() z; // this works

The template arg has a default arg, why require !() ??
This causes problems in meta code, where you want to create an instance of
some T, and T may be a normal type with no template args, in which case !()
is invalid, but a template type with default args should also be
acceptable, but it doesn't work because the meta code doesn't specify !().


The other case I am running in to is when I have 'struct S(T)' or 'class
C(T)', where T can be inferred from the constructor, but it isn't.

struct S(T)
{
  this(T t)
  {
    m = t;
  }

  T m;
}

int myThing;
auto s = S(myThing); // error!
auto s = S!(typeof(myThing))(myThing); // works, but horrible, and breaks
down again when used in place of non-template counterparts in meta code.


My main gripe here is that in both these cases which require special treatment for basic instantiation, they break down when used in meta code without loads of hacks and ugly mess throughout the meta to handle these cases.

Why these restrictions? Can these cases be fixed?
I have quite a lot of code that would be sanitised by these changes.


July 24, 2014
On Thursday, 24 July 2014 at 04:53:41 UTC, Manu via Digitalmars-d
wrote:
> struct S(size_t len = 10)
> {
>   ubyte[len] data;
> }
>
> S!100 x; // this works fine
> S y; // this doesn't work (!)
> S!() z; // this works
>
> The template arg has a default arg, why require !() ??

So that the type S!() is (easily) distinguishable from the
template S.

> This causes problems in meta code, where you want to create an instance of
> some T, and T may be a normal type with no template args, in which case !()
> is invalid, but a template type with default args should also be
> acceptable, but it doesn't work because the meta code doesn't specify !().

Add !() at the instantiation site.

> struct S(T)
> {
>   this(T t)
>   {
>     m = t;
>   }
>
>   T m;
> }
>
> int myThing;
> auto s = S(myThing); // error!
> auto s = S!(typeof(myThing))(myThing); // works, but horrible, and breaks
> down again when used in place of non-template counterparts in meta code.

Phobos goes with helper functions: S!T s(T)(T t) {return S!T(t);}
I'm not a fan either.
July 24, 2014
On 24 July 2014 16:02, anonymous via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 24 July 2014 at 04:53:41 UTC, Manu via Digitalmars-d wrote:
>
>  struct S(size_t len = 10)
>> {
>>   ubyte[len] data;
>> }
>>
>> S!100 x; // this works fine
>> S y; // this doesn't work (!)
>> S!() z; // this works
>>
>> The template arg has a default arg, why require !() ??
>>
>
> So that the type S!() is (easily) distinguishable from the
> template S.


Hmm.


 This causes problems in meta code, where you want to create an instance of
>> some T, and T may be a normal type with no template args, in which case
>> !()
>> is invalid, but a template type with default args should also be
>> acceptable, but it doesn't work because the meta code doesn't specify !().
>>
>
> Add !() at the instantiation site.


Then the code is broken for any type that isn't a template... it's a mutually exclusive situation.


 struct S(T)
>> {
>>   this(T t)
>>   {
>>     m = t;
>>   }
>>
>>   T m;
>> }
>>
>> int myThing;
>> auto s = S(myThing); // error!
>> auto s = S!(typeof(myThing))(myThing); // works, but horrible, and breaks
>> down again when used in place of non-template counterparts in meta code.
>>
>
> Phobos goes with helper functions: S!T s(T)(T t) {return S!T(t);}
> I'm not a fan either.
>

Why? Surely this is easy to implement?


July 24, 2014
"Manu via Digitalmars-d" <digitalmars-d@puremagic.com> wrote in message news:mailman.243.1406177619.32463.digitalmars-d@puremagic.com...

> The other case I am running in to is when I have 'struct S(T)' or 'class C(T)', where T > can be inferred from the constructor, but it isn't.
>
> struct S(T)
> {
>   this(T t)
>   {
>     m = t;
>   }
>
>   T m;
> }

Infer this:

struct S(T)
{
   static if (is(T == int))
       this(float x) {}
   static if (is(T == float))
       this(int x) {}
} 

July 24, 2014
On 24 July 2014 16:22, Daniel Murphy via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> "Manu via Digitalmars-d" <digitalmars-d@puremagic.com> wrote in message news:mailman.243.1406177619.32463.digitalmars-d@puremagic.com...
>
>  The other case I am running in to is when I have 'struct S(T)' or 'class
>> C(T)', where T > can be inferred from the constructor, but it isn't.
>>
>> struct S(T)
>> {
>>   this(T t)
>>   {
>>     m = t;
>>   }
>>
>>   T m;
>> }
>>
>
> Infer this:
>
> struct S(T)
> {
>    static if (is(T == int))
>        this(float x) {}
>    static if (is(T == float))
>        this(int x) {}
> }
>

I imagine that would be a compile error; the static if can't be resolved without knowing T, and prior to resolution of the static if, no constructors exist.

Also, the constructor args don't reference T anyway, so I see no reason why
it would ever want to try and deduce T in this situation.
In my example, the constructor implies T, in your example, T implies the
constructor... it doesn't make logical sense the way you present.

Personally, I wouldn't want the compiler to attempt to deduce T in this case you present, even if it were theoretically possible. It looks like the programmer intended something very specific in this case. I'd rather have compile errors when I pass incorrect things to explicit argument types.


July 24, 2014
On Thursday, 24 July 2014 at 07:01:09 UTC, Manu via Digitalmars-d wrote:
> I imagine that would be a compile error; the static if can't be resolved
> without knowing T, and prior to resolution of the static if, no
> constructors exist.

Please lay out this kind of logic in a DIP so it can actually be implemented. As I said in the enhancement request, the problem is defining the feature for the general case; that means defining it so the compiler knows how to proceed in all cases (including when to error). If we can't define this robustly it will become a pain in the ass for both users and compiler-writers.
July 24, 2014
On Thursday, 24 July 2014 at 04:53:41 UTC, Manu via Digitalmars-d wrote:
> I'm running into consistent problems with default args and argument
> deduction in templates.
> There seem to be 2 consistent classes of problem:
>
> struct S(size_t len = 10)
> {
>   ubyte[len] data;
> }
>
> S!100 x; // this works fine
> S y; // this doesn't work (!)
> S!() z; // this works
>
> The template arg has a default arg, why require !() ??
> This causes problems in meta code, where you want to create an instance of
> some T, and T may be a normal type with no template args, in which case !()
> is invalid, but a template type with default args should also be
> acceptable, but it doesn't work because the meta code doesn't specify !().

This opens a whole can of worms. It's very useful to be able to distinguish between templates and their instantiations. Seeing as D's alias system works on a pass-by-name system, you can't have a system where simply referring to a template instantiates it with no arguments.

Apart from anything else it would break *so* much code.
July 24, 2014
I think my understanding would be that 'S' isn't a struct. It's a template for a struct.
July 24, 2014
On 24 July 2014 19:37, John Colvin via Digitalmars-d < digitalmars-d@puremagic.com> wrote:

> On Thursday, 24 July 2014 at 04:53:41 UTC, Manu via Digitalmars-d wrote:
>
>> I'm running into consistent problems with default args and argument
>> deduction in templates.
>> There seem to be 2 consistent classes of problem:
>>
>> struct S(size_t len = 10)
>> {
>>   ubyte[len] data;
>> }
>>
>> S!100 x; // this works fine
>> S y; // this doesn't work (!)
>> S!() z; // this works
>>
>> The template arg has a default arg, why require !() ??
>> This causes problems in meta code, where you want to create an instance of
>> some T, and T may be a normal type with no template args, in which case
>> !()
>> is invalid, but a template type with default args should also be
>> acceptable, but it doesn't work because the meta code doesn't specify !().
>>
>
> This opens a whole can of worms. It's very useful to be able to distinguish between templates and their instantiations. Seeing as D's alias system works on a pass-by-name system, you can't have a system where simply referring to a template instantiates it with no arguments.
>
> Apart from anything else it would break *so* much code.
>

Isn't the call to the constructor enough to distinguish it is an instantiation rather than a reference to the template itself?

S is a template
S!() is a type
S(x,y,z) is a call to it's constructor, the expression has a type

What is the useful distinction between S!()(x,y,z) and S(x,y,z)? How does
either one of them make referring to the template 'S' difficult?
Is there some conflicting syntax where the parentheses mean something else
when S perceived as a template? Why isn't the same problem applicable to
function templates?


July 24, 2014
On 24 July 2014 21:25, Manu <turkeyman@gmail.com> wrote:

> On 24 July 2014 19:37, John Colvin via Digitalmars-d < digitalmars-d@puremagic.com> wrote:
>
>> On Thursday, 24 July 2014 at 04:53:41 UTC, Manu via Digitalmars-d wrote:
>>
>>> I'm running into consistent problems with default args and argument
>>> deduction in templates.
>>> There seem to be 2 consistent classes of problem:
>>>
>>> struct S(size_t len = 10)
>>> {
>>>   ubyte[len] data;
>>> }
>>>
>>> S!100 x; // this works fine
>>> S y; // this doesn't work (!)
>>> S!() z; // this works
>>>
>>> The template arg has a default arg, why require !() ??
>>> This causes problems in meta code, where you want to create an instance
>>> of
>>> some T, and T may be a normal type with no template args, in which case
>>> !()
>>> is invalid, but a template type with default args should also be
>>> acceptable, but it doesn't work because the meta code doesn't specify
>>> !().
>>>
>>
>> This opens a whole can of worms. It's very useful to be able to distinguish between templates and their instantiations. Seeing as D's alias system works on a pass-by-name system, you can't have a system where simply referring to a template instantiates it with no arguments.
>>
>> Apart from anything else it would break *so* much code.
>>
>
> Isn't the call to the constructor enough to distinguish it is an instantiation rather than a reference to the template itself?
>
> S is a template
> S!() is a type
> S(x,y,z) is a call to it's constructor, the expression has a type
>
> What is the useful distinction between S!()(x,y,z) and S(x,y,z)? How does
> either one of them make referring to the template 'S' difficult?
> Is there some conflicting syntax where the parentheses mean something else
> when S perceived as a template? Why isn't the same problem applicable to
> function templates?
>

Ack! Sorry! I misread, your response as being related to the constructor case, not the default arg case >_<

I see the problem with the default arg case. It's a real shame, because it
has rather annoying side effects in generic code, and it doesn't appear to
follow the same logical rules as with functions.
This is precisely the sort of thing Scott Myers would be unhappy about...
Someone will need to 'explain' this for years to come, I don't think it's
intuitive ;)


« First   ‹ Prev
1 2