www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Template argument deduction and default args

reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
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.
Jul 23 2014
next sibling parent reply "anonymous" <anonymous example.com> writes:
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.
Jul 23 2014
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
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?
Jul 23 2014
parent "anonymous" <anonymous example.com> writes:
On Thursday, 24 July 2014 at 06:17:26 UTC, Manu via Digitalmars-d
wrote:
 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:
[...]
  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.
I mean, don't accept both types and templates. Demand types in the meta code, and do the instantiation of S when instantiating the meta code: metaCode!(S!()). I understand that it's a bit ugly, but I don't see the bigger issue here. Do you have an example where there's more to it than just having to add !() somewhere? I do see the problem with the other case, S(T) {this(T) {...}}, though. You can't just do metaCode!(S!()) there.
Jul 24 2014
prev sibling next sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"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) {} }
Jul 23 2014
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
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.
Jul 24 2014
parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
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.
Jul 24 2014
prev sibling next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
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.
Jul 24 2014
next sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
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?
Jul 24 2014
parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 24 July 2014 at 11:25:28 UTC, Manu via Digitalmars-d 
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?
That's a somewhat different consideration. I agree that this working might be a good thing: struct S(int n = 10) { int a; } auto s = S(4); where the type of the struct does not depend on the type of the parameters to the constructor. However, I'm not sold that full-on implicit template instantiation should apply to members. How would we deal with this situation: struct S(T) { this(T v) {} } auto s = S(4); //yay, compiler worked out S!int later on someone does this: struct S(T) { this(Q v) if(is(Q : T)) {} } for whatever reason. Now all those nice constructor calls to S(someValue) are broken. I dunno, somehow the implicit behaviour seems ok when it's very local but it feels dodgy at the level of separation that there can be between constructors and their types.
Jul 24 2014
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 24 July 2014 21:48, John Colvin via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 That's a somewhat different consideration.

 I agree that this working might be a good thing:

 struct S(int n = 10)
 {
     int a;
 }

 auto s = S(4);

 where the type of the struct does not depend on the type of the parameters
 to the constructor. However, I'm not sold that full-on implicit template
 instantiation should apply to members. How would we deal with this
 situation:

 struct S(T)
 {
     this(T v) {}
 }

 auto s = S(4); //yay, compiler worked out S!int

 later on someone does this:

 struct S(T)
 {
     this(Q v) if(is(Q : T)) {}
 }
That doesn't make any sense though. "Q : T" is not a 1:1 relationship, so no deduction is possible there. It makes equally little sense in the function case. void f(T)(Q v) if(is(Q : T)) { } I only suggest that what would work for the function should equally work for the struct/class constructor. for whatever reason.
 Now all those nice constructor calls to S(someValue) are broken.
Likewise if someone changed a function similarly. I see no distinction from the function case, which I'm sure everyone agrees is perfectly good how it is? I dunno, somehow the implicit behaviour seems ok when it's very local but
 it feels dodgy at the level of separation that there can be between
 constructors and their types.
Not really sure what you mean. Constructors are absolutely tied to their type, that's the point of a constructor.
Jul 24 2014
prev sibling next sibling parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
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 ;)
Jul 24 2014
prev sibling parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 24 July 2014 21:30, Manu <turkeyman gmail.com> wrote:

 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 ;)
Although... the more I think about it, the more I wonder why it matters if the syntax is ambiguous when the name is taken in isolation, that never actually happens... Why can't it just mean 'either the template, or the default arg instantiation', and be resolved when it's actually used? Is it possible for templates or types to both appear in the same context and create an actual ambiguity? What would that expression look like? The only place I can imagine a conflict could occur would be within an is() expression, but I'm not sure... can a uninstantiated template be used in an is() expression where a type would also be a meaningful fit? Generally, templates do this: T!() And types do this: T var; It's clear syntactically from 'T!()' that T is not a default args instantiation of T, because it's involved in a template instantiation expression. It's also clear from 'T var' that T is not a template, because a variable needs to have a type.
Jul 24 2014
next sibling parent reply "John Colvin" <john.loughran.colvin gmail.com> writes:
On Thursday, 24 July 2014 at 11:39:13 UTC, Manu via Digitalmars-d 
wrote:
 On 24 July 2014 21:30, Manu <turkeyman gmail.com> wrote:

 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 ;)
Although... the more I think about it, the more I wonder why it matters if the syntax is ambiguous when the name is taken in isolation, that never actually happens... Why can't it just mean 'either the template, or the default arg instantiation', and be resolved when it's actually used? Is it possible for templates or types to both appear in the same context and create an actual ambiguity? What would that expression look like? The only place I can imagine a conflict could occur would be within an is() expression, but I'm not sure... can a uninstantiated template be used in an is() expression where a type would also be a meaningful fit? Generally, templates do this: T!() And types do this: T var; It's clear syntactically from 'T!()' that T is not a default args instantiation of T, because it's involved in a template instantiation expression. It's also clear from 'T var' that T is not a template, because a variable needs to have a type.
Seeing as templates can resolve to other templates, mixin templates, values, functions and types, the situation is complicated.
Jul 24 2014
parent Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 24 July 2014 22:03, John Colvin via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 On Thursday, 24 July 2014 at 11:39:13 UTC, Manu via Digitalmars-d wrote:
Although... the more I think about it, the more I wonder why it matters if
 the syntax is ambiguous when the name is taken in isolation, that never
 actually happens...
 Why can't it just mean 'either the template, or the default arg
 instantiation', and be resolved when it's actually used?
 Is it possible for templates or types to both appear in the same context
 and create an actual ambiguity? What would that expression look like?
 The only place I can imagine a conflict could occur would be within an
 is()
 expression, but I'm not sure... can a uninstantiated template be used in
 an
 is() expression where a type would also be a meaningful fit?

 Generally, templates do this:
   T!()
 And types do this:
   T var;

 It's clear syntactically from 'T!()' that T is not a default args
 instantiation of T, because it's involved in a template instantiation
 expression.
 It's also clear from 'T var' that T is not a template, because a variable
 needs to have a type.
Seeing as templates can resolve to other templates, mixin templates, values, functions and types, the situation is complicated.
This is just FUD without any case for consideration. All of those different resolutions imply different syntactic contexts. I'm still not sure of a case where a genuine ambiguity arises.
Jul 24 2014
prev sibling parent reply "Daniel Murphy" <yebbliesnospam gmail.com> writes:
"Manu via Digitalmars-d" <digitalmars-d puremagic.com> wrote in message 
news:mailman.274.1406201953.32463.digitalmars-d puremagic.com...

Is it possible for templates or types to both appear in the same context and 
create an actual ambiguity? What would that expression look like?

alias X = T!U;

What is U?  Did you mean to pass the template or the type? 
Jul 24 2014
parent reply Manu via Digitalmars-d <digitalmars-d puremagic.com> writes:
On 24 July 2014 22:15, Daniel Murphy via Digitalmars-d <
digitalmars-d puremagic.com> wrote:

 "Manu via Digitalmars-d" <digitalmars-d puremagic.com> wrote in message
 news:mailman.274.1406201953.32463.digitalmars-d puremagic.com...


 Is it possible for templates or types to both appear in the same context
 and create an actual ambiguity? What would that expression look like?

 alias X = T!U;

 What is U?  Did you mean to pass the template or the type?
I didn't specify, and I don't think this code requires that I do. T will resolve this situation naturally. Is it 'template T(X)'? Obviously the type. Is it 'template T(alias X)'? this could be the type or the template, but since the template is more inclusive, it should be that. Within T's definition, if T is passed as the template, then the opportunity is retained to go either way as is necessary when the context becomes explicit. I actually think it works quite elegantly. What have I missed?
Jul 24 2014
parent Jacob Carlborg <doob me.com> writes:
On 24/07/14 17:11, Manu via Digitalmars-d wrote:

 I didn't specify, and I don't think this code requires that I do.

 T will resolve this situation naturally.
 Is it 'template T(X)'? Obviously the type.
 Is it 'template T(alias X)'? this could be the type or the template, but
 since the template is more inclusive, it should be that. Within T's
 definition, if T is passed as the template, then the opportunity is
 retained to go either way as is necessary when the context becomes explicit.
 I actually think it works quite elegantly. What have I missed?
I'm pretty sure I've asked the same question as you did. I also got similar replays. To me it's quite simple. In every case it will be the template, as it is now. Except when called with arguments as a constructor. -- /Jacob Carlborg
Jul 25 2014
prev sibling parent "w0rp" <devw0rp gmail.com> writes:
I think my understanding would be that 'S' isn't a struct. It's a 
template for a struct.
Jul 24 2014