Thread overview
Templated struct doesn't need the parameterized type in return type definitions?
Mar 08, 2011
Andrej Mitrovic
Mar 08, 2011
spir
Mar 08, 2011
David Nadlinger
Re: Templated struct doesn't need the parameterized type in return
Mar 08, 2011
bearophile
Mar 08, 2011
Andrej Mitrovic
Mar 09, 2011
Jonathan M Davis
Mar 09, 2011
Nick Treleaven
March 08, 2011
import std.stdio;
import std.traits;
import std.exception;

struct CheckedInt(N) if (isIntegral!N)
{
    private N value;

    ref CheckedInt opUnary(string op)() if (op == "++")
    {
        enforce(value != value.max);
        ++value;
        return this;
    }

    this(N _value)
    {
        value = _value;
    }
}

I didn't know you could define a return type of a templated struct without defining the type it is parameterized on. I mean this line:

ref CheckedInt opUnary(string op)() if (op == "++")

I thought for sure I always had to write the parameterized type like so:

ref CheckedInt!(N) opUnary(string op)() if (op == "++")

So I guess this really isn't a question but more of a "oh, I didn't know you could do that". In fact I rarely see this kind of code in Phobos, most of the time the parameterized type is specified in these types of cases. Is this feature described somewhere, because I must have missed it if it is?

As a side-note, auto ref is useful in this case, which is pretty cool:
auto ref opUnary(string op)() if (op == "++")
March 08, 2011
On Tue, 08 Mar 2011 12:06:08 -0500, Andrej Mitrovic <none@none.none> wrote:

> import std.stdio;
> import std.traits;
> import std.exception;
>
> struct CheckedInt(N) if (isIntegral!N)
> {
>     private N value;
>    ref CheckedInt opUnary(string op)() if (op == "++")
>     {
>         enforce(value != value.max);
>         ++value;
>         return this;
>     }
>    this(N _value)
>     {
>         value = _value;
>     }
> }
>
> I didn't know you could define a return type of a templated struct without defining the type it is parameterized on. I mean this line:
>
> ref CheckedInt opUnary(string op)() if (op == "++")
>
> I thought for sure I always had to write the parameterized type like so:
>
> ref CheckedInt!(N) opUnary(string op)() if (op == "++")
>
> So I guess this really isn't a question but more of a "oh, I didn't know you could do that". In fact I rarely see this kind of code in Phobos, most of the time the parameterized type is specified in these types of cases. Is this feature described somewhere, because I must have missed it if it is?

It is described, but not directly.

Look on this page:

http://www.digitalmars.com/d/2.0/template.html

  From there we have these two descriptions:

------------------------

If a template has exactly one member in it, and the name of that member is
the same as the template name, that member is assumed to be referred to in
a template instantiation:
template Foo(T)
{
      T Foo;	// declare variable Foo of type T
}

void test()
{
      Foo!(int) = 6;	// instead of Foo!(int).Foo
}

------------------------

If a template declares exactly one member, and that member is a class with
the same name as the template:
template Bar(T)
{
      class Bar
      {
	T member;
      }
}

then the semantic equivalent, called a ClassTemplateDeclaration can be
written as:
class Bar(T)
{
      T member;
}

------------------------

Also note that structs have the same description.

So if you think about it, your code is equivalent to:

template CheckedInt(N) if(isIntegral!N)
{
   struct CheckedInt
   {
      ...
   }
}

If you look at it this way, it makes complete sense that within the struct that's within the template, the struct can refer to itself without the specific instantiation parameters.

I think this should really be laid out properly in the docs.  I discovered this "trick" while writing dcollections by accident and thought it so awesome that I changed all my code which self-returned (quite a bit).

-Steve
March 08, 2011
On 03/08/2011 06:20 PM, Steven Schveighoffer wrote:
> On Tue, 08 Mar 2011 12:06:08 -0500, Andrej Mitrovic <none@none.none> wrote:
>
>> import std.stdio;
>> import std.traits;
>> import std.exception;
>>
>> struct CheckedInt(N) if (isIntegral!N)
>> {
>> private N value;
>> ref CheckedInt opUnary(string op)() if (op == "++")
>> {
>> enforce(value != value.max);
>> ++value;
>> return this;
>> }
>> this(N _value)
>> {
>> value = _value;
>> }
>> }
>>
>> I didn't know you could define a return type of a templated struct without
>> defining the type it is parameterized on. I mean this line:
>>
>> ref CheckedInt opUnary(string op)() if (op == "++")
>>
>> I thought for sure I always had to write the parameterized type like so:
>>
>> ref CheckedInt!(N) opUnary(string op)() if (op == "++")
>>
>> So I guess this really isn't a question but more of a "oh, I didn't know you
>> could do that". In fact I rarely see this kind of code in Phobos, most of the
>> time the parameterized type is specified in these types of cases. Is this
>> feature described somewhere, because I must have missed it if it is?
>
> It is described, but not directly.
>
> Look on this page:
>
> http://www.digitalmars.com/d/2.0/template.html
>
>  From there we have these two descriptions:
>
> ------------------------
>
> If a template has exactly one member in it, and the name of that member is
> the same as the template name, that member is assumed to be referred to in
> a template instantiation:
> template Foo(T)
> {
> T Foo; // declare variable Foo of type T
> }
>
> void test()
> {
> Foo!(int) = 6; // instead of Foo!(int).Foo
> }
>
> ------------------------
>
> If a template declares exactly one member, and that member is a class with
> the same name as the template:
> template Bar(T)
> {
> class Bar
> {
> T member;
> }
> }
>
> then the semantic equivalent, called a ClassTemplateDeclaration can be
> written as:
> class Bar(T)
> {
> T member;
> }
>
> ------------------------
>
> Also note that structs have the same description.
>
> So if you think about it, your code is equivalent to:
>
> template CheckedInt(N) if(isIntegral!N)
> {
> struct CheckedInt
> {
> ...
> }
> }
>
> If you look at it this way, it makes complete sense that within the struct
> that's within the template, the struct can refer to itself without the specific
> instantiation parameters.
>
> I think this should really be laid out properly in the docs. I discovered this
> "trick" while writing dcollections by accident and thought it so awesome that I
> changed all my code which self-returned (quite a bit).
>
> -Steve

I don't share your enthusiasm, Steven, for this feature (which I did not know). In fact, I tend to consider it a mis-feature. Yet another syntactic special-case for special cases in the language. In this case, there are even 3 ways to write the same thing:
	CheckedInt
	CheckedInt!N
	CheckedInt!(N)
And note these variants are low-level ones, morphological rather than syntactic properly speaking.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

March 08, 2011
On 3/8/11 8:20 PM, spir wrote:
> […] Yet another
> syntactic special-case for special cases in the language. In this case,
> there are even 3 ways to write the same thing: […]

I don't quite get how you think this would be a syntactic special case. As Steve pointed out, »class Foo(T) {}« is merely syntax sugar for »template Foo(T) { class Foo{} }«, and because of this, it would rather be a special case *not* to allow referring to Foo using just that name. When considering this, don't forget that templates in D are little more than parametrized, named scopes.

I guess you could argue that »class Foo(T)« as a shorthand is an unnecessary special case, but I very much prefer the benefit in brevity here…

David
March 08, 2011
On Tue, 08 Mar 2011 14:20:40 -0500, spir <denis.spir@gmail.com> wrote:

> On 03/08/2011 06:20 PM, Steven Schveighoffer wrote:
>> On Tue, 08 Mar 2011 12:06:08 -0500, Andrej Mitrovic <none@none.none> wrote:
>>
>>> import std.stdio;
>>> import std.traits;
>>> import std.exception;
>>>
>>> struct CheckedInt(N) if (isIntegral!N)
>>> {
>>> private N value;
>>> ref CheckedInt opUnary(string op)() if (op == "++")
>>> {
>>> enforce(value != value.max);
>>> ++value;
>>> return this;
>>> }
>>> this(N _value)
>>> {
>>> value = _value;
>>> }
>>> }
>>>
>>> I didn't know you could define a return type of a templated struct without
>>> defining the type it is parameterized on. I mean this line:
>>>
>>> ref CheckedInt opUnary(string op)() if (op == "++")
>>>
>>> I thought for sure I always had to write the parameterized type like so:
>>>
>>> ref CheckedInt!(N) opUnary(string op)() if (op == "++")
>>>
>>> So I guess this really isn't a question but more of a "oh, I didn't know you
>>> could do that". In fact I rarely see this kind of code in Phobos, most of the
>>> time the parameterized type is specified in these types of cases. Is this
>>> feature described somewhere, because I must have missed it if it is?
>>
>> It is described, but not directly.
>>
>> Look on this page:
>>
>> http://www.digitalmars.com/d/2.0/template.html
>>
>>  From there we have these two descriptions:
>>
>> ------------------------
>>
>> If a template has exactly one member in it, and the name of that member is
>> the same as the template name, that member is assumed to be referred to in
>> a template instantiation:
>> template Foo(T)
>> {
>> T Foo; // declare variable Foo of type T
>> }
>>
>> void test()
>> {
>> Foo!(int) = 6; // instead of Foo!(int).Foo
>> }
>>
>> ------------------------
>>
>> If a template declares exactly one member, and that member is a class with
>> the same name as the template:
>> template Bar(T)
>> {
>> class Bar
>> {
>> T member;
>> }
>> }
>>
>> then the semantic equivalent, called a ClassTemplateDeclaration can be
>> written as:
>> class Bar(T)
>> {
>> T member;
>> }
>>
>> ------------------------
>>
>> Also note that structs have the same description.
>>
>> So if you think about it, your code is equivalent to:
>>
>> template CheckedInt(N) if(isIntegral!N)
>> {
>> struct CheckedInt
>> {
>> ...
>> }
>> }
>>
>> If you look at it this way, it makes complete sense that within the struct
>> that's within the template, the struct can refer to itself without the specific
>> instantiation parameters.
>>
>> I think this should really be laid out properly in the docs. I discovered this
>> "trick" while writing dcollections by accident and thought it so awesome that I
>> changed all my code which self-returned (quite a bit).
>>
>> -Steve
>
> I don't share your enthusiasm, Steven, for this feature (which I did not know). In fact, I tend to consider it a mis-feature. Yet another syntactic special-case for special cases in the language. In this case, there are even 3 ways to write the same thing:
> 	CheckedInt
> 	CheckedInt!N
> 	CheckedInt!(N)
> And note these variants are low-level ones, morphological rather than syntactic properly speaking.

Here's another thing I found in dcollections which caught me off guard, and which I was glad to be rid of when I switched to not parameterizing the names of self returns:

class Collection(T)
{
   Collection!(T) add(T t) { ...; return this; }
   // 20 other functions like add...
}

"Hey, wouldn't it be cool if I could add a custom allocator to all classes!?"...

class Collection(T, alloc = DefaultAllocator!T)
{
   Collection!(T) add(T t) { ...; return this; }
   // 20 other now subtly incorrect functions like add...
}

See the problem?

-Steve
March 08, 2011
David Nadlinger:

> and because of this, it would rather be a special case *not* to allow referring to Foo using just that name.

Right. On the other hand the current straightforward design leads to some bugs too, the give bad error messages: http://d.puremagic.com/issues/show_bug.cgi?id=3950

Bye,
bearophile
March 08, 2011
On 3/8/11, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> See the problem?

Yup. Btw, does auto ref still suffer from any bugs that I should know about? I've heard it had issues.
March 09, 2011
On Tuesday, March 08, 2011 15:31:37 Andrej Mitrovic wrote:
> On 3/8/11, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> > See the problem?
> 
> Yup. Btw, does auto ref still suffer from any bugs that I should know about? I've heard it had issues.

I'm not sure that it works correctly with properties at the moment. It _does_ appear in the docs now though.

- Jonathan M Davis
March 09, 2011
On Tue, 08 Mar 2011 15:25:27 -0500, Steven Schveighoffer wrote:

> "Hey, wouldn't it be cool if I could add a custom allocator to all classes!?"...
> 
> class Collection(T, alloc = DefaultAllocator!T) {
>     Collection!(T) add(T t) { ...; return this; } // 20 other now subtly
>     incorrect functions like add...
> }
> 
> See the problem?

This seems like a good reason to keep allowing the feature. It would be nice if it could be documented clearly somewhere, maybe here:

http://www.digitalmars.com/d/2.0/template.html#ClassTemplateDeclaration