www.digitalmars.com         C & C++   DMDScript  

D - template proposal

reply "Walter" <walter digitalmars.com> writes:
www.digitalmars.com/template.html

Notice how short it is <g>. Ok, what did I miss?
Aug 21 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html
 Notice how short it is <g>. Ok, what did I miss?

First thing I missed was the right url <sigh> www.digitalmars.com/d/template.html
Aug 21 2002
next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html
 Notice how short it is <g>. Ok, what did I miss?

First thing I missed was the right url <sigh> www.digitalmars.com/d/template.html

max() and min() were presented to me as a classic problem that C++ templates were trying to solve. The beauty was that there only needed to be one declaration, and all types automatically got a max() function. It was a substitute for C's template solution. I don't see how D templates can do max and min, unless you explicitly instantiate them: in Max(int) maxint; val3 = maxint.max(val1,val2); That seems like an ugly solution to me, especially since (as I understand it), we can't use the same instantiation name for multiple different instantiations. If we're going to have to have a "maxint" template instantiation that's separate from a "maxfloat" and all the others, we might as well just define functions and save ourselves 4 characters of typing when we call it. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Aug 21 2002
next sibling parent reply "anderson" <anderson firestar.com.au> writes:
That sounds more like a functional template then a class template. Parhaps D
needs that as well?

"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63793A.93407771 deming-os.org...
 Walter wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html
 Notice how short it is <g>. Ok, what did I miss?

First thing I missed was the right url <sigh> www.digitalmars.com/d/template.html

max() and min() were presented to me as a classic problem that C++ templates were trying to solve. The beauty was that there only needed to be one declaration, and all types automatically got a max() function. It was a substitute for C's template solution. I don't see how D templates can do max and min, unless you explicitly instantiate them: in Max(int) maxint; val3 = maxint.max(val1,val2); That seems like an ugly solution to me, especially since (as I understand it), we can't use the same instantiation name for multiple different instantiations. If we're going to have to have a "maxint" template instantiation that's separate from a "maxfloat" and all the others, we might as well just define functions and save ourselves 4 characters of typing when we call it. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]

Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:ak04p5$t3u$1 digitaldaemon.com...
 That sounds more like a functional template then a class template. Parhaps

 needs that as well?

D does not have a distinction between class and function templates. An entire group of declarations of any kind can be within a template body, including classes, functions, variables, enums, nested templates, etc.
Aug 21 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak0efd$1lp2$2 digitaldaemon.com...

 D does not have a distinction between class and function templates. An
 entire group of declarations of any kind can be within a template body,
 including classes, functions, variables, enums, nested templates, etc.

It's like a "template namespace", which is something I've always wanted in C++. Salutaciones, JCAB
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak16eg$2q07$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ak0efd$1lp2$2 digitaldaemon.com...
 D does not have a distinction between class and function templates. An
 entire group of declarations of any kind can be within a template body,
 including classes, functions, variables, enums, nested templates, etc.

in C++.

Exactly!
Aug 21 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63793A.93407771 deming-os.org...
 max() and min() were presented to me as a classic problem that C++
 templates were trying to solve.  The beauty was that there only needed
 to be one declaration, and all types automatically got a max()
 function.  It was a substitute for C's template solution.  I don't see
 how D templates can do max and min, unless you explicitly instantiate
 them:
     in Max(int) maxint; val3 = maxint.max(val1,val2);
 That seems like an ugly solution to me, especially since (as I
 understand it), we can't use the same instantiation name for multiple
 different instantiations.  If we're going to have to have a "maxint"
 template instantiation that's separate from a "maxfloat" and all the
 others, we might as well just define functions and save ourselves 4
 characters of typing when we call it.

You are correct that D does not do C++'s implicit instantiation, in D, instantiation must be explicit. The reason is I have come to believe that much of the complexity and problems with C++ templates comes from implicit instantiation - this is complexity in both specification and use. In practice, I find that many programs use typedef's to try and reduce the complexity and approximate explicit instantiation.
Aug 21 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D63793A.93407771 deming-os.org...
 
max() and min() were presented to me as a classic problem that C++
templates were trying to solve.  The beauty was that there only needed
to be one declaration, and all types automatically got a max()
function.  It was a substitute for C's template solution.  I don't see
how D templates can do max and min, unless you explicitly instantiate
them:
    in Max(int) maxint; val3 = maxint.max(val1,val2);
That seems like an ugly solution to me, especially since (as I
understand it), we can't use the same instantiation name for multiple
different instantiations.  If we're going to have to have a "maxint"
template instantiation that's separate from a "maxfloat" and all the
others, we might as well just define functions and save ourselves 4
characters of typing when we call it.

You are correct that D does not do C++'s implicit instantiation, in D, instantiation must be explicit. The reason is I have come to believe that much of the complexity and problems with C++ templates comes from implicit instantiation - this is complexity in both specification and use. In practice, I find that many programs use typedef's to try and reduce the complexity and approximate explicit instantiation.

That was only part of the problem. I was also concerned about namespace cluttering (maxint, maxfloat, etc. instead of just a single "max").
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63C9DD.3060203 deming-os.org...
 Walter wrote:
 You are correct that D does not do C++'s implicit instantiation, in D,
 instantiation must be explicit. The reason is I have come to believe


 much of the complexity and problems with C++ templates comes from


 instantiation - this is complexity in both specification and use. In
 practice, I find that many programs use typedef's to try and reduce the
 complexity and approximate explicit instantiation.

cluttering (maxint, maxfloat, etc. instead of just a single "max").

The advantage of that is you'll get much more reasonable error messages, rather than things like: Error: foo<<<t>bar<<>>>const T>>>***<<T,Q><int <<<>>> bar<q,int*><10> <g>
Aug 21 2002
prev sibling next sibling parent reply Russ Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:

 "Walter" <walter digitalmars.com> wrote in message
 news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html
 Notice how short it is <g>. Ok, what did I miss?

First thing I missed was the right url <sigh> www.digitalmars.com/d/template.html

In the "Argument Deduction" section, I don't understand the Bar(D, D : D[]) example. Does this mean that you have only one template parameter, "D", and you have to specify it in 2 different ways? If not, then what does it mean? -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ]
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D637991.FF91430E deming-os.org...
 In the "Argument Deduction" section,  I don't understand the Bar(D, D :
 D[]) example.  Does this mean that you have only one template parameter,
 "D", and you have to specify it in 2 different ways?  If not, then what
 does it mean?

That defines, to use C++ terminology, a "partial specialization", and is equivalent to the C++: template<class D> int Bar<D, D[]>() { } It means there are two arguments to the template, and to instantiate it you need to specify both. While this doesn't make a whole lot of sense by itself, it is to distinguish Bar(D,D[]) from another template Bar(D,E).
Aug 21 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Russ Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D637991.FF91430E deming-os.org...
 
In the "Argument Deduction" section,  I don't understand the Bar(D, D :
D[]) example.  Does this mean that you have only one template parameter,
"D", and you have to specify it in 2 different ways?  If not, then what
does it mean?

That defines, to use C++ terminology, a "partial specialization", and is equivalent to the C++: template<class D> int Bar<D, D[]>() { } It means there are two arguments to the template, and to instantiate it you need to specify both. While this doesn't make a whole lot of sense by itself, it is to distinguish Bar(D,D[]) from another template Bar(D,E).

What is the difference between template Bar(D, D : D[]) {...} and template Bar(D : D{}) {...} ?
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63CB4E.30009 deming-os.org...
 Walter wrote:
 That defines, to use C++ terminology, a "partial specialization", and is
 equivalent to the C++:

     template<class D> int Bar<D, D[]>() { }

 It means there are two arguments to the template, and to instantiate it


 need to specify both. While this doesn't make a whole lot of sense by
 itself, it is to distinguish Bar(D,D[]) from another template Bar(D,E).

What is the difference between template Bar(D, D : D[]) {...} and template Bar(D : D{}) {...} ?

The latter is illegal, as D{} is not a type.
Aug 21 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D63CB4E.30009 deming-os.org...
 
Walter wrote:

That defines, to use C++ terminology, a "partial specialization", and is
equivalent to the C++:

    template<class D> int Bar<D, D[]>() { }

It means there are two arguments to the template, and to instantiate it


need to specify both. While this doesn't make a whole lot of sense by
itself, it is to distinguish Bar(D,D[]) from another template Bar(D,E).

What is the difference between template Bar(D, D : D[]) {...} and template Bar(D : D{}) {...} ?

The latter is illegal, as D{} is not a type.

Oops, meant D[] on the latter one
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63D965.4040809 deming-os.org...
What is the difference between
     template Bar(D, D : D[]) {...}
and
     template Bar(D : D{}) {...}
?



In that case, it serves to distinguish: instance Bar(int) x1; from: instance Bar(int, int[]) x2;
Aug 21 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D63D965.4040809 deming-os.org...
 
What is the difference between
    template Bar(D, D : D[]) {...}
and
    template Bar(D : D{}) {...}
?

The latter is illegal, as D{} is not a type.

Oops, meant D[] on the latter one

In that case, it serves to distinguish: instance Bar(int) x1; from: instance Bar(int, int[]) x2;

And the difference between the two is?
Aug 22 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Thu, 22 Aug 2002 14:02:13 -0700 Russell Lewis 
<spamhole-2001-07-16 deming-os.org> wrote:

 In that case, it serves to distinguish:
 
     instance Bar(int) x1;
 
 from:
 
     instance Bar(int, int[]) x2;

And the difference between the two is?

The first one gets one argument, the second gets two. =)
Aug 22 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374913480596065 news.digitalmars.com...
 On Thu, 22 Aug 2002 14:02:13 -0700 Russell Lewis
 <spamhole-2001-07-16 deming-os.org> wrote:
 In that case, it serves to distinguish:
     instance Bar(int) x1;
 from:
     instance Bar(int, int[]) x2;



Yes, that's all it is. Think of it like function overloading.
Aug 23 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:CFN374913480596065 news.digitalmars.com...
 
On Thu, 22 Aug 2002 14:02:13 -0700 Russell Lewis
<spamhole-2001-07-16 deming-os.org> wrote:

In that case, it serves to distinguish:
    instance Bar(int) x1;
from:
    instance Bar(int, int[]) x2;

And the difference between the two is?

The first one gets one argument, the second gets two. =)

Yes, that's all it is. Think of it like function overloading.

Kind of like void foo(int a,int b) in { assert(a == b); } {...} ??? If that's the case, why are we reusing the "Bar" name rather than just defining a new template name that only takes one argument???
Aug 23 2002
next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Fri, 23 Aug 2002 11:21:40 -0700 Russell Lewis 
<spamhole-2001-07-16 deming-os.org> wrote:

 If that's the case, why are we reusing the "Bar" name rather than just 
 defining a new template name that only takes one argument???

It was just an example! It doesn't have to be useful or even have sense. =)
Aug 23 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D667D34.3090803 deming-os.org...
 Walter wrote:
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:CFN374913480596065 news.digitalmars.com...

On Thu, 22 Aug 2002 14:02:13 -0700 Russell Lewis
<spamhole-2001-07-16 deming-os.org> wrote:

In that case, it serves to distinguish:
    instance Bar(int) x1;
from:
    instance Bar(int, int[]) x2;

And the difference between the two is?

The first one gets one argument, the second gets two. =)

Yes, that's all it is. Think of it like function overloading.

Kind of like void foo(int a,int b) in { assert(a == b); } {...} ??? If that's the case, why are we reusing the "Bar" name rather than just defining a new template name that only takes one argument???

Because Bar(int, int[]) is a specialization of Bar(T, U).
Aug 23 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D667D34.3090803 deming-os.org...
 
Walter wrote:

"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374913480596065 news.digitalmars.com...


On Thu, 22 Aug 2002 14:02:13 -0700 Russell Lewis
<spamhole-2001-07-16 deming-os.org> wrote:


In that case, it serves to distinguish:
   instance Bar(int) x1;
from:
   instance Bar(int, int[]) x2;

And the difference between the two is?

The first one gets one argument, the second gets two. =)

Yes, that's all it is. Think of it like function overloading.

Kind of like void foo(int a,int b) in { assert(a == b); } {...} ??? If that's the case, why are we reusing the "Bar" name rather than just defining a new template name that only takes one argument???

Because Bar(int, int[]) is a specialization of Bar(T, U).

Ok, got you so far. So why declare it as template Bar(D, D : D[]) {...} rather than template Bar(D, UNUSED_PARAM : D[]) {...} ??? The former looks like you're declaring the same parameter twice, with two different usages.
Aug 26 2002
parent "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D6A69CC.5050106 deming-os.org...
 Because Bar(int, int[]) is a specialization of Bar(T, U).

template Bar(D, D : D[]) {...} rather than template Bar(D, UNUSED_PARAM : D[]) {...} ??? The former looks like you're declaring the same parameter twice, with two different usages.

It could be: (D, :D[]) but I thought the : might appear invisible like that.
Aug 26 2002
prev sibling parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Walter" <walter digitalmars.com> wrote in message
 news:ajvgsb$6gt$1 digitaldaemon.com...
 
www.digitalmars.com/template.html
Notice how short it is <g>. Ok, what did I miss?

First thing I missed was the right url <sigh> www.digitalmars.com/d/template.html

Can you nest templates? Would it be useful to be able to derive one template from another (to add memebers, not to override them)?
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63DF06.6030202 deming-os.org...
 Can you nest templates?

Yes.
 Would it be useful to be able to derive one
 template from another (to add memebers, not to override them)?

Uh-oh! Maybe for V3 of the language!
Aug 21 2002
prev sibling next sibling parent reply "anderson" <anderson firestar.com.au> writes:
Looks pretty complete for my needs (although I'm no template expert). Simple
yet powerful. Just a few questions.

"
template Foo(T);
 in Foo(int); // T is deduced to be int
 in Foo(char*); // T is deduced to be char*
"
I don't understand this where is the InstanceIdentifier? Or is this some
type of overloaded function call?

I guess this is a stupid question, but will it support operator overloading?

"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html

 Notice how short it is <g>. Ok, what did I miss?

Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"anderson" <anderson firestar.com.au> wrote in message
news:ajvr5q$i69$1 digitaldaemon.com...
 Looks pretty complete for my needs (although I'm no template expert).

 yet powerful. Just a few questions.

 "
 template Foo(T);
  in Foo(int); // T is deduced to be int
  in Foo(char*); // T is deduced to be char*
 "
 I don't understand this where is the InstanceIdentifier? Or is this some
 type of overloaded function call?

I forgot to put in the InstanceIdentifier. Oops!
 I guess this is a stupid question, but will it support operator

Sure.
Aug 21 2002
prev sibling next sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
1. I would prefer something other than 'in' for 'instantiation'.  I realize that
'instance' might be annoying to type, but I will never be able to look at 'in'
and see anything except 'the opposite of out'.  I could go with 'inst', which
contains enough of the word to suggest 'instance' to me.  (I have similar
problems with the standard C++ STL notation of InIt for an Input Iterator -- it
just looks like an initializer to me...)

2. How do I make two variables of type Foo(int)?  According to the
documentation, this appears to be explicitly disallowed -- "Multiple
instantiations of a TemplateDeclaration with the same TemplateParameterList all
will refer to the same instantiation."  If I make a RedBlackTree(T), and then
find myself needing two separate trees that both hold char[]s, how can I do
that?  I *could* typedef two different names for char[]s and make separate
instances that way, but that just obscures the issue.  Readers of the code then
have to go look up my typedef to find out that it is just a char[] with no
additional functionality.

3. It might be useful to have some way to say that some template parameters are
"like" or "derived from" other template parameters:

template Cage(Base, Derived like Base) {/*whatever*/}

Then at instantiation:

inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from Bird
inst Cage(Bird, Bird);         // OK
inst Cage(Bird, Collie);       // Error - Collie does not derive from Bird
inst Cage(Bird, Animal);       // Error - inheritance order is backwards

I'm not sure how useful this is in practice.  I know Eiffel has a similar
system, and Bertrand Meyer gets really excited about it, but I was never certain
that I saw the benefit of this over using Base*s and letting vtables handle it.

4. I would really like constrained genericity.  Similar to the above, but when
you say a parameter is "like" some base class, the base class doesn't need to be
another parameter to the template.

template Calculator(T like NUMERIC) {/*whatever*/}

In this case, NUMERIC is basically an interface.  The standard argument against
this is that if someone tries to instantiate the template with a class that is
not NUMERIC, something will fail when Calculator tries to use one of the NUMERIC
members.  But it is possible (even if unlikely) that a non-NUMERIC class could
accidentally implement enough of NUMERIC to let it work, but not work the way
Calculator expects.  Calculator is a bad example of this, since anyone providing
NUMERIC-style members probably is doing numerical work -- but for other
templates and other interfaces, the issue is not as clear.  The other benefit is
self-documentation.  By placing it in the code, the developer makes it clear
what he/she is requiring for the template parameter.  It becomes part of the
contract.

I have no particular attachment to the word "like" in either of the above
points.  It just happened to be the first thing I thought of.  "implements"
might not be a bad choice, although it is rather wordy ( T implements NUMERIC ).
"isa" or "is_a" might work too ( T isa NUMERIC ).

Mac
Aug 21 2002
next sibling parent Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Mac Reiter wrote:
 1. I would prefer something other than 'in' for 'instantiation'.  I realize
that
 'instance' might be annoying to type, but I will never be able to look at 'in'
 and see anything except 'the opposite of out'.  I could go with 'inst', which
 contains enough of the word to suggest 'instance' to me.  (I have similar
 problems with the standard C++ STL notation of InIt for an Input Iterator -- it
 just looks like an initializer to me...)

Hear, Hear
 2. How do I make two variables of type Foo(int)?  According to the
 documentation, this appears to be explicitly disallowed -- "Multiple
 instantiations of a TemplateDeclaration with the same TemplateParameterList all
 will refer to the same instantiation."  If I make a RedBlackTree(T), and then
 find myself needing two separate trees that both hold char[]s, how can I do
 that?  I *could* typedef two different names for char[]s and make separate
 instances that way, but that just obscures the issue.  Readers of the code then
 have to go look up my typedef to find out that it is just a char[] with no
 additional functionality.

As I understand it, the code would be in RedBlackTree(char[]) RedBlackTree_of_char_array; RedBlackTree_of_char_array var1; RedBlackTree_of_char_array var2;
Aug 21 2002
prev sibling next sibling parent reply Suporte Internet <suporte spica.mps.com.br> writes:
Mac Reiter <Mac_member pathlink.com> wrote:
 1. I would prefer something other than 'in' for 'instantiation'.  I realize
that
 'instance' might be annoying to type, but I will never be able to look at 'in'
 and see anything except 'the opposite of out'.  I could go with 'inst', which
 contains enough of the word to suggest 'instance' to me.  (I have similar
 problems with the standard C++ STL notation of InIt for an Input Iterator -- it

Why not use the keyword "typedef" ? This is not what is doing ? You are creating a new type based on a template ?
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Suporte Internet" <suporte spica.mps.com.br> wrote in message
news:ak0dea$1ich$1 digitaldaemon.com...
 Why not use the keyword "typedef" ?
 This is not what is doing ? You are creating a new type based on a
 template ?

typedef is a good idea, the trouble comes from distinguishing a typedef from a declaration just by the syntax: typedef foo(int*) f;
Aug 21 2002
parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 "Suporte Internet" <suporte spica.mps.com.br> wrote in message
 news:ak0dea$1ich$1 digitaldaemon.com...
 
Why not use the keyword "typedef" ?
This is not what is doing ? You are creating a new type based on a
template ?

typedef is a good idea, the trouble comes from distinguishing a typedef from a declaration just by the syntax: typedef foo(int*) f;

You could go the old C route: typedef template foo(int*) f;
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D63CB8E.3030608 deming-os.org...
 Walter wrote:
 "Suporte Internet" <suporte spica.mps.com.br> wrote in message
 news:ak0dea$1ich$1 digitaldaemon.com...
Why not use the keyword "typedef" ?
This is not what is doing ? You are creating a new type based on a
template ?



 a declaration just by
 the syntax:
     typedef foo(int*) f;

typedef template foo(int*) f;

I think I'd rather use the keyword 'instance' <g>.
Aug 21 2002
parent reply "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Walter" <walter digitalmars.com> wrote in message
news:ak0j61$1r42$2 digitaldaemon.com...
 I think I'd rather use the keyword 'instance' <g>.

Couldn't it cause some confusion with instances of classes, which are objects? Instances of a template is an interely different thing. How about "concrete", meaning that you make the abstract template concrete by supplying types. Regards, Martin M. Pedersen
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ak0sto$ic1$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ak0j61$1r42$2 digitaldaemon.com...
 I think I'd rather use the keyword 'instance' <g>.

Couldn't it cause some confusion with instances of classes, which are objects? Instances of a template is an interely different thing. How about "concrete", meaning that you make the abstract template concrete by supplying types.

It could, but instantiations of templates, as well as instantiations of classes, are both commonly referred to as instances. Your point is likely why Ada instantiates templates with the 'new' keyword.
Aug 21 2002
prev sibling next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak09pv$1b4k$1 digitaldaemon.com...
 1. I would prefer something other than 'in' for 'instantiation'.  I

 'instance' might be annoying to type, but I will never be able to look at

 and see anything except 'the opposite of out'.  I could go with 'inst',

 contains enough of the word to suggest 'instance' to me.  (I have similar
 problems with the standard C++ STL notation of InIt for an Input

 just looks like an initializer to me...)

I was afraid that 'instance' just was too long. An alternative is to use 'new' sort of like Ada. But you might be right.
 2. How do I make two variables of type Foo(int)?

You don't - Foo is a template, not a type. A type could be declared inside the template: template Foo(T) { typedef T abc; } instance Foo(int) myfoo; // Declare multiple variables: myfoo.abc var1; myfoo.abc var2;
 According to the
 documentation, this appears to be explicitly disallowed -- "Multiple
 instantiations of a TemplateDeclaration with the same

 will refer to the same instantiation."  If I make a RedBlackTree(T), and

 find myself needing two separate trees that both hold char[]s, how can I

 that?  I *could* typedef two different names for char[]s and make separate
 instances that way, but that just obscures the issue.  Readers of the code

 have to go look up my typedef to find out that it is just a char[] with no
 additional functionality.

In that case, it sounds like one should write: template RedBlackTree(T) { class Foo { char[] s; } } instance RedBlackTree(whatever) mytree; // Now create multiple independent trees, each with their own data: mytree.Foo f = new mytree(Foo); mytree.Foo g = new mytree(Foo);
 3. It might be useful to have some way to say that some template

 "like" or "derived from" other template parameters:

 template Cage(Base, Derived like Base) {/*whatever*/}

 Then at instantiation:

 inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from Bird
 inst Cage(Bird, Bird);         // OK
 inst Cage(Bird, Collie);       // Error - Collie does not derive from Bird
 inst Cage(Bird, Animal);       // Error - inheritance order is backwards

 I'm not sure how useful this is in practice.  I know Eiffel has a similar
 system, and Bertrand Meyer gets really excited about it, but I was never

 that I saw the benefit of this over using Base*s and letting vtables

That could possibly be handled by a contract: template Cage(Base, Derived) { class Foo { Base b; Derived d; invariant { assert(cast(Base)d != null); } } }
 4. I would really like constrained genericity.  Similar to the above, but

 you say a parameter is "like" some base class, the base class doesn't need

 another parameter to the template.

 template Calculator(T like NUMERIC) {/*whatever*/}

 In this case, NUMERIC is basically an interface.  The standard argument

 this is that if someone tries to instantiate the template with a class

 not NUMERIC, something will fail when Calculator tries to use one of the

 members.  But it is possible (even if unlikely) that a non-NUMERIC class

 accidentally implement enough of NUMERIC to let it work, but not work the

 Calculator expects.  Calculator is a bad example of this, since anyone

 NUMERIC-style members probably is doing numerical work -- but for other
 templates and other interfaces, the issue is not as clear.  The other

 self-documentation.  By placing it in the code, the developer makes it

 what he/she is requiring for the template parameter.  It becomes part of

 contract.

That is a good suggestion. Note that there are already ways to constrain it to be, say, an array type: template Foo(T : T[])
Aug 21 2002
parent reply Mac Reiter <Mac_member pathlink.com> writes:
 2. How do I make two variables of type Foo(int)?

You don't - Foo is a template, not a type. A type could be declared inside the template: template Foo(T) { typedef T abc; } instance Foo(int) myfoo; // Declare multiple variables: myfoo.abc var1; myfoo.abc var2;

Thank you. I hadn't quite grokked that a template was a thing entirely to itself. The extra level of indirection (myfoo.abc rather than just abc(int)) had not really clicked. Now that I see that, the RedBlack(char) question goes away. I really like being able to make an arbitrary amount of stuff inside a template, all based on the same instantiation parameters. I'll have to think about it some more before I can give any useful feedback... Mac
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak0ltg$2hjd$1 digitaldaemon.com...
 Thank you.  I hadn't quite grokked that a template was a thing entirely to
 itself.  The extra level of indirection (myfoo.abc rather than just

 had not really clicked.  Now that I see that, the RedBlack(char) question

 away.

Yup. It's perilous to break from the C++ mindset on how it should work <g>.
 I really like being able to make an arbitrary amount of stuff inside a

 all based on the same instantiation parameters.  I'll have to think about

 some more before I can give any useful feedback...

It has the nice feature of simply chucking the whole C++ messy distinctions between template classes, template functions, member template functions (or is it template member functions?), etc. In many more complex cases of this, it can get really tricky for the C++ compiler to parse which is which. In D, the template syntax is a no-brainer to implement. (Semantic analysis is a little harder!)
Aug 21 2002
parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak0rom$app$2 digitaldaemon.com...
 "Mac Reiter" <Mac_member pathlink.com> wrote in message
 news:ak0ltg$2hjd$1 digitaldaemon.com...
 Thank you.  I hadn't quite grokked that a template was a thing entirely


 itself.  The extra level of indirection (myfoo.abc rather than just

 had not really clicked.  Now that I see that, the RedBlack(char)


 goes
 away.

Yup. It's perilous to break from the C++ mindset on how it should work

But do we want that extra indirection, after all? In practice you usually need class templates. You will end up typedef-ing everything, to avoid the burden of the unnecessary indirection. typedef RedBlackTree instance RBT_Template(char).TheClassInside; Makes me feel like Java, with all the superfluous indirections. And class templates also allow for function templates as static member functions. Sorry if this was already metioned. I don't have the time to read all the hundreds of mails in this thread. Yours, Sandor
Aug 26 2002
parent "Walter" <walter digitalmars.com> writes:
"Sandor Hojtsy" <hojtsy index.hu> wrote in message
news:akd51e$1hvo$1 digitaldaemon.com...
 But do we want that extra indirection, after all?
 In practice you usually need class templates.
 You will end up typedef-ing everything, to avoid the burden of the
 unnecessary indirection.

 typedef   RedBlackTree  instance RBT_Template(char).TheClassInside;
 Makes me feel like Java, with all the superfluous indirections.
 And class templates also allow for function templates as static member
 functions.

Yes, it's a little more typing, but I think that it makes things a lot clearer.
Aug 26 2002
prev sibling next sibling parent Jason Mills <jmills cs.mun.ca> writes:
Mac Reiter wrote:
 1. I would prefer something other than 'in' for 'instantiation'.  I realize
that
 'instance' might be annoying to type, but I will never be able to look at 'in'
 and see anything except 'the opposite of out'.  I could go with 'inst', which
 contains enough of the word to suggest 'instance' to me.  (I have similar
 problems with the standard C++ STL notation of InIt for an Input Iterator -- it
 just looks like an initializer to me...)

I agree. In addition, "in" is already used for "in" parameters and "in" contracts.
 2. How do I make two variables of type Foo(int)?  According to the
 documentation, this appears to be explicitly disallowed -- "Multiple
 instantiations of a TemplateDeclaration with the same TemplateParameterList all
 will refer to the same instantiation."  If I make a RedBlackTree(T), and then
 find myself needing two separate trees that both hold char[]s, how can I do
 that?  I *could* typedef two different names for char[]s and make separate
 instances that way, but that just obscures the issue.  Readers of the code then
 have to go look up my typedef to find out that it is just a char[] with no
 additional functionality.
 
 3. It might be useful to have some way to say that some template parameters are
 "like" or "derived from" other template parameters:
 
 template Cage(Base, Derived like Base) {/*whatever*/}
 
 Then at instantiation:
 
 inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from Bird
 inst Cage(Bird, Bird);         // OK
 inst Cage(Bird, Collie);       // Error - Collie does not derive from Bird
 inst Cage(Bird, Animal);       // Error - inheritance order is backwards
 
 I'm not sure how useful this is in practice.  I know Eiffel has a similar
 system, and Bertrand Meyer gets really excited about it, but I was never
certain
 that I saw the benefit of this over using Base*s and letting vtables handle it.
 
 4. I would really like constrained genericity.  Similar to the above, but when
 you say a parameter is "like" some base class, the base class doesn't need to
be
 another parameter to the template.
 
 template Calculator(T like NUMERIC) {/*whatever*/}
 
 In this case, NUMERIC is basically an interface.  The standard argument against
 this is that if someone tries to instantiate the template with a class that is
 not NUMERIC, something will fail when Calculator tries to use one of the
NUMERIC
 members.  But it is possible (even if unlikely) that a non-NUMERIC class could
 accidentally implement enough of NUMERIC to let it work, but not work the way
 Calculator expects.  Calculator is a bad example of this, since anyone
providing
 NUMERIC-style members probably is doing numerical work -- but for other
 templates and other interfaces, the issue is not as clear.  The other benefit
is
 self-documentation.  By placing it in the code, the developer makes it clear
 what he/she is requiring for the template parameter.  It becomes part of the
 contract.
 
 I have no particular attachment to the word "like" in either of the above
 points.  It just happened to be the first thing I thought of.  "implements"
 might not be a bad choice, although it is rather wordy ( T implements NUMERIC
).
 "isa" or "is_a" might work too ( T isa NUMERIC ).
 
 Mac
 
 

Aug 21 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak09pv$1b4k$1 digitaldaemon.com...
 3. It might be useful to have some way to say that some template

 "like" or "derived from" other template parameters:

 template Cage(Base, Derived like Base) {/*whatever*/}

 Then at instantiation:

 inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from Bird
 inst Cage(Bird, Bird);         // OK
 inst Cage(Bird, Collie);       // Error - Collie does not derive from Bird
 inst Cage(Bird, Animal);       // Error - inheritance order is backwards

 I'm not sure how useful this is in practice.  I know Eiffel has a similar
 system, and Bertrand Meyer gets really excited about it, but I was never

 that I saw the benefit of this over using Base*s and letting vtables

 4. I would really like constrained genericity.  Similar to the above, but

 you say a parameter is "like" some base class, the base class doesn't need

 another parameter to the template.

 template Calculator(T like NUMERIC) {/*whatever*/}

 In this case, NUMERIC is basically an interface.  The standard argument

 this is that if someone tries to instantiate the template with a class

 not NUMERIC, something will fail when Calculator tries to use one of the

 members.  But it is possible (even if unlikely) that a non-NUMERIC class

 accidentally implement enough of NUMERIC to let it work, but not work the

 Calculator expects.  Calculator is a bad example of this, since anyone

 NUMERIC-style members probably is doing numerical work -- but for other
 templates and other interfaces, the issue is not as clear.  The other

 self-documentation.  By placing it in the code, the developer makes it

 what he/she is requiring for the template parameter.  It becomes part of

 contract.

It occurs to me that this does fit in with the D template syntax: template Calculator(T : NUMERIC) { ... } means that T can be either NUMERIC or a class derived from NUMERIC (if NUMERIC is a class or interface). Your case 3 would be: template Cage(B, D : B) {/*whatever*/} which would constrain D to be the same as B or derived from B.
Aug 21 2002
next sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <ak0rom$app$3 digitaldaemon.com>, Walter says...
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak09pv$1b4k$1 digitaldaemon.com...
 3. It might be useful to have some way to say that some template

 "like" or "derived from" other template parameters:

 template Cage(Base, Derived like Base) {/*whatever*/}

 Then at instantiation:

 inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from Bird
 inst Cage(Bird, Bird);         // OK
 inst Cage(Bird, Collie);       // Error - Collie does not derive from Bird
 inst Cage(Bird, Animal);       // Error - inheritance order is backwards


 template Calculator(T like NUMERIC) {/*whatever*/}


It occurs to me that this does fit in with the D template syntax:

    template Calculator(T : NUMERIC) { ... }

means that T can be either NUMERIC or a class derived from NUMERIC (if
NUMERIC is a class or interface). Your case 3 would be:

    template Cage(B, D : B) {/*whatever*/}

which would constrain D to be the same as B or derived from B.

Even better. The 'T : NUMERIC' syntax is much like the Eiffel syntax I was coming from. I didn't know if it would work that way in D or not. The examples with : were demonstrating partial template specialization (or the D equivalent) and the ability to pull types out of pointers to / arrays of types. I didn't know if the constraint system supported general purpose "is a" constraints, including direct and indirect inheritance. There appears to be some semantic difference: template Calc(T : NUMERIC) template Other(T : T[]) In the first case, I would instantiate Calc with a class that was derived from NUMERIC (or NUMERIC itself, if NUMERIC is concrete). But in the second case, I instantiate Calc with an array, and the compiler extracts the type. If these two cases behaved identically, then either: 1. Take the first example as the standard -- Other must now be instantiated with a single variable, and the type of that variable is required to support the interface of an array of that type. T is required to act as if it is an array of T, which it can't do. 2. Take the second example as the standard -- Calc must now be instantiated with a NUMERIC, and the D compiler will deduce an appropriate type for T. I suppose this would be possible with RTTI, or by allowing the compiler to take derivatives of NUMERIC directly... I guess my mental hangup is that 'T:NUMERIC' means "T must support the NUMERIC interface" (derive from it, whatever), but 'T:T[]' means "given a T[], extract T". Having had Eiffel training in constrained genericity, I tend to favor the "must support" semantics, but I am trying to maintain an open mind... I know that you've been thinking about this for quite some time, and I also know that it is difficult to come up with small simple examples that illustrate the intended usage of some of these features. (My examples when trying to show this stuff to my coworkers is almost always horrible) The 'template X(T,T:T[])' examples so far seem like they shouldn't need the second parameter, since if you know T you can always make a T[] inside the template. In C++ you need this sort of thing in case the user is using smart array classes, smart pointer classes, iterators, etc. But in D, arrays are arrays, pointers are pointers, etc, so there doesn't seem to be the need to allow the user to specify *how* to make an array out of the base type. Could you give us an example (even if it is somewhat complicated) of the case you were considering when you made the 'template X(T,T:T[])' behavior? I'm sure there's a reason that I'm just overlooking. Thanks, Mac
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak0tm6$njk$1 digitaldaemon.com...
 There appears to be some semantic difference:

 template Calc(T : NUMERIC)
 template Other(T : T[])

 In the first case, I would instantiate Calc with a class that was derived

 NUMERIC (or NUMERIC itself, if NUMERIC is concrete).  But in the second

 instantiate Calc with an array, and the compiler extracts the type.  If

 two cases behaved identically, then either:

 1. Take the first example as the standard -- Other must now be

 a single variable, and the type of that variable is required to support

 interface of an array of that type.  T is required to act as if it is an

 of T, which it can't do.

 2. Take the second example as the standard -- Calc must now be

 a NUMERIC, and the D compiler will deduce an appropriate type for T.  I

 this would be possible with RTTI, or by allowing the compiler to take
 derivatives of NUMERIC directly...

 I guess my mental hangup is that 'T:NUMERIC' means "T must support the

 interface" (derive from it, whatever), but 'T:T[]' means "given a T[],

 T".  Having had Eiffel training in constrained genericity, I tend to favor

 "must support" semantics, but I am trying to maintain an open mind...

You're right, there is a subtle difference there. I think if the rule were if T is in the specialization, then the value of T is extracted. If T is not in the specialization, T is whatever the whole specialization type is (or derived from it).
 I know that you've been thinking about this for quite some time, and I

 that it is difficult to come up with small simple examples that illustrate

 intended usage of some of these features.  (My examples when trying to

 stuff to my coworkers is almost always horrible)  The 'template

 examples so far seem like they shouldn't need the second parameter, since

 know T you can always make a T[] inside the template.  In C++ you need

 of thing in case the user is using smart array classes, smart pointer

 iterators, etc.  But in D, arrays are arrays, pointers are pointers, etc,

 there doesn't seem to be the need to allow the user to specify *how* to

 array out of the base type.  Could you give us an example (even if it is
 somewhat complicated) of the case you were considering when you made the
 'template X(T,T:T[])' behavior?  I'm sure there's a reason that I'm just
 overlooking.

I don't have a good example. One thing about C++, there seems to be a veritable army of very smart people discovering clever things to do with C++ templates that weren't anticipated in the initial design of templates. The X(T,T:T[]) falls out of the specialization syntax, so there's no obvious reason to disallow it.
Aug 21 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak11s6$1lac$1 digitaldaemon.com...

 You're right, there is a subtle difference there. I think if the rule were
 if T is in the specialization, then the value of T is extracted. If T is

 in the specialization, T is whatever the whole specialization type is (or
 derived from it).

Or you can use a different operator to make it even simpler to parse. => and := come to mind... Still, using runtime interfaces for this sounds a little wrong to me. One thing that I don't like is not having numeric/value template parameters. And, now that we're at it, "TemplateInstantiations are always performed in the scope of where the TemplateDeclaration is declared, with the addition of the template parameters being declared as aliases for their deduced types", so what's the semantics of this: --- void func(int) { ... } template Tpl(T) { void anotherfunc(T t) { func(t); } } void func(bool) { ... } instance Tpl(bool) tplBool; // How? tplBool.anotherfunc(false); // How? --- I guess that with the non-need for function prototypes, this particular example is solvable in D. But I'm thinking especially of templates defined in one module and used in another. Salutaciones, JCAB
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak1fat$2c87$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ak11s6$1lac$1 digitaldaemon.com...
 You're right, there is a subtle difference there. I think if the rule


 if T is in the specialization, then the value of T is extracted. If T is

 in the specialization, T is whatever the whole specialization type is


 derived from it).


 and := come to mind...
    Still, using runtime interfaces for this sounds a little wrong to me.

It's still compile time, just a bit extra work for the semantic pass. Not too tough, C++ templates are far worse <g>. I just have to adapt the argument type deduction code from that, meanwhile whacking off about 90% of it.
    One thing that I don't like is not having numeric/value template
 parameters.

Just use them as class members, and pass the class type.
    And, now that we're at it, "TemplateInstantiations are always performed
 in the scope of where the TemplateDeclaration is declared, with the

 of the template parameters being declared as aliases for their deduced
 types", so what's the semantics of this:

 ---
 void func(int) { ... }

 template Tpl(T) {
   void anotherfunc(T t) {
     func(t);
   }
 }

 void func(bool) { ... }

 instance Tpl(bool) tplBool; // How?

 tplBool.anotherfunc(false); // How?

It doesn't matter if some functions are before and some after the use of it, the compiler sees all of them when doing the semantic analysis. This just doesn't have the problems/bugs inherent in the C++ model of not knowing about anything ahead of it in the source file. Hence, the call func(t) will resolve to func(bool), regardless of the lexical positioning of func(int) and func(bool).
    I guess that with the non-need for function prototypes, this particular
 example is solvable in D. But I'm thinking especially of templates defined
 in one module and used in another.

If func() is defined in a different module than template Tpl() and is not visible in the scope of template Tpl(), then you'll get a "func undefined" message when the instance Tpl(bool) is compiled regardless of whether func() is visible at the scope of instance Tpl(bool) or not. This is fundamentally different than the way C++ works - C++ tries to do it both ways, with some names getting resolved at template declaration scope, and some at template instantiation scope, depending on whether a name is "type-dependent" or not.
Aug 21 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak1k42$j5f$1 digitaldaemon.com...

    Still, using runtime interfaces for this sounds a little wrong to me.

It's still compile time, just a bit extra work for the semantic pass.

Hmmm... I guess...
 It doesn't matter if some functions are before and some after the use of

 the compiler sees all of them when doing the semantic analysis.

Ok... I figured that.
    I guess that with the non-need for function prototypes, this


 example is solvable in D. But I'm thinking especially of templates


 in one module and used in another.

If func() is defined in a different module than template Tpl() and is not visible in the scope of template Tpl(), then you'll get a "func undefined" message when the instance Tpl(bool) is compiled regardless of whether

 is visible at the scope of instance Tpl(bool) or not.

So you can't really use templates taken from a library with user-defined types unless those user defined types are already known to the library?
 This is fundamentally different than the way C++ works - C++ tries to do

 both ways, with some names getting resolved at template declaration scope,
 and some at template instantiation scope, depending on whether a name is
 "type-dependent" or not.

Yes, and we agree that's bad. Still, unless you have a clever answer to this, I can't see how your solution is good enough. I know you don't like forward declarations, but they might be warranted in the case of templates: template Tpl(T) { exists void func(T); // forward declaration... void anotherfunc(T t) { func(t); } } This would allow you to eliminate all problems with dependent names. It'll also allow the compiler to do even better error messages for invalid instantiations. In a way, you can consider this as compile-time contracts for templates. Salutaciones, JCAB
Aug 22 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Thu, 22 Aug 2002 12:58:44 -0700 "Juan Carlos Arevalo Baeza" 
<jcab JCABs-Rumblings.com> wrote:

    So you can't really use templates taken from a library with user-defined
 types unless those user defined types are already known to the library?

You can, as long as you pass those types as template parameters.
Aug 22 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374913474843634 news.digitalmars.com...

    So you can't really use templates taken from a library with


 types unless those user defined types are already known to the library?

You can, as long as you pass those types as template parameters.

But not unless those types were visible at the point where the template was declared. So: --- mylibrary.d template Tpl(T) { ... } --- --- myprogram.d using mylibrary; class MyClass { ... } instance Tpl(MyClass) TplMyClass; // Fails!!! --- That's what Walter seems to be saying. That the instantiation fails because MyClass is not visible in mylibrary.d. And that's what I have a problem with. Salutaciones, JCAB
Aug 23 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Fri, 23 Aug 2002 10:50:45 -0700 "Juan Carlos Arevalo Baeza" 
<jcab JCABs-Rumblings.com> wrote:

 "Pavel Minayev" <evilone omen.ru> wrote in message
 --- mylibrary.d
 template Tpl(T) { ... }
 ---
 
 --- myprogram.d
 using mylibrary;
 
 class MyClass { ... }
 
 instance Tpl(MyClass) TplMyClass; // Fails!!!
 ---

I think you CAN do it. What Walter meant is that you can use MyClass directly from Tpl if it is declared in the same module with Tpl. Otherwise, you have to pass it as a template argument.
Aug 23 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374919430425 news.digitalmars.com...

 --- mylibrary.d
 template Tpl(T) { ... }
 ---

 --- myprogram.d
 using mylibrary;

 class MyClass { ... }

 instance Tpl(MyClass) TplMyClass; // Fails!!!
 ---

I think you CAN do it. What Walter meant is that you can use MyClass

 from
 Tpl if it is declared in the same module with Tpl. Otherwise, you have to

 it
 as a template argument.

Well, that's not what I was asking to Walter. "TemplateInstantiations are always performed in the scope of where the TemplateDeclaration is declared, with the addition of the template parameters being declared as aliases for their deduced types" Let's update my second example: --- mylibrary.d template Tpl(T) { void tplfunc(T t) { func(t); } } --- --- myprogram.d using mylibrary; class MyClass { ... } void func(MyClass) { ... } instance Tpl(MyClass) TplMyClass; // Fails!!! --- Salutaciones, JCAB
Aug 23 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak687r$2h6e$1 digitaldaemon.com...
 "TemplateInstantiations are always performed
 in the scope of where the TemplateDeclaration is declared, with the

 of the template parameters being declared as aliases for their deduced
 types"

    Let's update my second example:

 --- mylibrary.d
 template Tpl(T) {
   void tplfunc(T t) {
     func(t);
   }
 }
 ---

 --- myprogram.d
 using mylibrary;

 class MyClass { ... }

 void func(MyClass) { ... }

 instance Tpl(MyClass) TplMyClass; // Fails!!!

Yes, that will fail. To make it work, func(MyClass) would need to be a class member of some class passed as a template argument.
Aug 23 2002
parent Pavel Minayev <evilone omen.ru> writes:
On Fri=2C 23 Aug 2002 20=3A40=3A15 -0700 =22Walter=22
=3Cwalter=40digitalmars=2Ecom=3E wrote=3A

=3E=3E    Let's update my second example=3A
=3E=3E
=3E=3E --- mylibrary=2Ed
=3E=3E template Tpl=28T=29 {
=3E=3E   void tplfunc=28T t=29 {
=3E=3E     func=28t=29=3B
=3E=3E   }
=3E=3E }
=3E=3E ---
=3E=3E
=3E=3E --- myprogram=2Ed
=3E=3E using mylibrary=3B
=3E=3E
=3E=3E class MyClass { =2E=2E=2E }
=3E=3E
=3E=3E void func=28MyClass=29 { =2E=2E=2E }
=3E=3E
=3E=3E instance Tpl=28MyClass=29 TplMyClass=3B =2F=2F Fails!!!
=3E 
=3E Yes=2C that will fail=2E To make it work=2C func=28MyClass=29 would need to
be a class
=3E member of some class passed as a template argument=2E
 
To fix it=3A

=09template Tpl=28T=2C Func=29
=09{
=09=09void tplfunc=28T t=29 { Func=2Ecall=28t=29=3B }
=09}

=09class MyClass { =2E=2E=2E }

=09void func=28MyClass=29 { =2E=2E=2E }

=09class Func=5FMyClass
=09{
=09=09static void call=28MyClass x=29 { func x=3B }
=09}

=09instance Tpl=28MyClass=2C Func=5FMyClass=29 TplMyClass=3B    =2F=2F Works! 
Aug 24 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak5s95$24i6$1 digitaldaemon.com...
    But not unless those types were visible at the point where the template
 was declared. So:

 --- mylibrary.d
 template Tpl(T) { ... }
 ---

 --- myprogram.d
 using mylibrary;

 class MyClass { ... }

 instance Tpl(MyClass) TplMyClass; // Fails!!!
 ---

    That's what Walter seems to be saying. That the instantiation fails
 because MyClass is not visible in mylibrary.d. And that's what I have a
 problem with.

Anything passed as a template argument *is* visible.
Aug 23 2002
prev sibling parent "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak3fd7$2dhu$1 digitaldaemon.com...
    So you can't really use templates taken from a library with

 types unless those user defined types are already known to the library?

Yes you can, if you pass the user defined type as a template argument.
    I know you don't like forward declarations, but they might be warranted
 in the case of templates:

 template Tpl(T) {
   exists void func(T); // forward declaration...
   void anotherfunc(T t) {
     func(t);
   }
 }

    This would allow you to eliminate all problems with dependent names.
 It'll also allow the compiler to do even better error messages for invalid
 instantiations. In a way, you can consider this as compile-time contracts
 for templates.

You can do things like: template Tpl(T) { T.func(t); } Won't that fit the bill?
Aug 23 2002
prev sibling next sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
This sounds like Pascal declaration syntax.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:ak0rom$app$3 digitaldaemon.com...
 "Mac Reiter" <Mac_member pathlink.com> wrote in message
 news:ak09pv$1b4k$1 digitaldaemon.com...
 3. It might be useful to have some way to say that some template

 "like" or "derived from" other template parameters:

 template Cage(Base, Derived like Base) {/*whatever*/}

 Then at instantiation:

 inst Cage(Bird, Cockateel);    // OK, assuming Cockateel derives from


 inst Cage(Bird, Bird);         // OK
 inst Cage(Bird, Collie);       // Error - Collie does not derive from


 inst Cage(Bird, Animal);       // Error - inheritance order is backwards

 I'm not sure how useful this is in practice.  I know Eiffel has a


 system, and Bertrand Meyer gets really excited about it, but I was never

 that I saw the benefit of this over using Base*s and letting vtables

 4. I would really like constrained genericity.  Similar to the above,


 when
 you say a parameter is "like" some base class, the base class doesn't


 to be
 another parameter to the template.

 template Calculator(T like NUMERIC) {/*whatever*/}

 In this case, NUMERIC is basically an interface.  The standard argument

 this is that if someone tries to instantiate the template with a class

 not NUMERIC, something will fail when Calculator tries to use one of the

 members.  But it is possible (even if unlikely) that a non-NUMERIC class

 accidentally implement enough of NUMERIC to let it work, but not work


 way
 Calculator expects.  Calculator is a bad example of this, since anyone

 NUMERIC-style members probably is doing numerical work -- but for other
 templates and other interfaces, the issue is not as clear.  The other

 self-documentation.  By placing it in the code, the developer makes it

 what he/she is requiring for the template parameter.  It becomes part of

 contract.

It occurs to me that this does fit in with the D template syntax: template Calculator(T : NUMERIC) { ... } means that T can be either NUMERIC or a class derived from NUMERIC (if NUMERIC is a class or interface). Your case 3 would be: template Cage(B, D : B) {/*whatever*/} which would constrain D to be the same as B or derived from B.

Aug 22 2002
parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <ak36u4$23sj$1 digitaldaemon.com>, Sean L. Palmer says...
This sounds like Pascal declaration syntax.

Sean

Actually taken from Eiffel, at least when I mentioned it. It does happen to be the same syntax as Pascal's: var I : INTEGER; I suppose that for UML people, some simulation of the inheritance arrow might be better: template Calc(T=>NUMERIC) or template Calc(T->NUMERIC) I don't know if this causes lexing/parsing problems or not. It is about as close as I can think of to UML's method of showing that T inherits from NUMERIC. I personally don't have a problem with the ':' syntax and its simularity to Pascal. I suppose it might cause problems for Delphi programmers, but anyone used to any older form of Pascal is going to be going through so many mental contortions anyway that they probably wouldn't even notice it. Even if they did, it is still vaguely appropriate: I : INTEGER; means that I "is an" INTEGER. T : NUMERIC means that T "is a" NUMERIC. It just happens to be the case that in OOP the "is a" relationship is much more flexible than it was in Pascal... I'm cool with almost any form, as long as it provides compile time type constraints for the template parameters. Mac (the following is for reference...)
"Walter" <walter digitalmars.com> wrote in message
news:ak0rom$app$3 digitaldaemon.com...
 "Mac Reiter" <Mac_member pathlink.com> wrote in message
 news:ak09pv$1b4k$1 digitaldaemon.com...


 It occurs to me that this does fit in with the D template syntax:

     template Calculator(T : NUMERIC) { ... }

 means that T can be either NUMERIC or a class derived from NUMERIC (if
 NUMERIC is a class or interface). Your case 3 would be:

     template Cage(B, D : B) {/*whatever*/}

 which would constrain D to be the same as B or derived from B.


Aug 22 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Thu, 22 Aug 2002 18:25:46 +0000 (UTC) Mac Reiter <Mac_member pathlink.com> 
wrote:

 I : INTEGER;
 
 means that I "is an" INTEGER.
 
 T : NUMERIC
 
 means that T "is a" NUMERIC.  It just happens to be the case that in OOP the 

 a" relationship is much more flexible than it was in Pascal...

Yes, exactly. Being a hardcore Pascal fan once, I must admit that I like this syntax partially because it reminds me of good old TP days... =)
Aug 22 2002
parent "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374909701815972 news.digitalmars.com...

 T : NUMERIC

Yes, exactly. Being a hardcore Pascal fan once, I must admit that I like

 syntax partially
 because it reminds me of good old TP days... =)

He he... I've come to like it better than the C/C++ syntax. Much clearer, much less ambiguous, and it rationalizes the use of type expressions, by making them look exactly the same wether you're casting, or specifying a template argument, or declaring a variable of such type. Salutaciones, JCAB
Aug 22 2002
prev sibling parent reply Russell Lewis <spamhole-2001-07-16 deming-os.org> writes:
Walter wrote:
 It occurs to me that this does fit in with the D template syntax:
 
     template Calculator(T : NUMERIC) { ... }
 
 means that T can be either NUMERIC or a class derived from NUMERIC (if
 NUMERIC is a class or interface). Your case 3 would be:
 
     template Cage(B, D : B) {/*whatever*/}
 
 which would constrain D to be the same as B or derived from B.

template List(X) { class List {...}; }; template Thingy(A, List(A).List) {...}; However, since we can't refer to template instances that way - we have to instantiate them explictly - I don't see any way to do this with the current template syntax. Do you have any ideas how?
Aug 22 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
news:3D655282.5030402 deming-os.org...
 I could see somebody wanting to do something like this:

 template List(X) {
      class List {...};
 };
 template Thingy(A, List(A).List) {...};

 However, since we can't refer to template instances that way - we have
 to instantiate them explictly - I don't see any way to do this with the
 current template syntax.  Do you have any ideas how?

I could see doing that with: template Thingy(A, instance List(A).List) {...};
Aug 23 2002
parent reply "Sandor Hojtsy" <hojtsy index.hu> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak757j$dr2$1 digitaldaemon.com...
 "Russell Lewis" <spamhole-2001-07-16 deming-os.org> wrote in message
 news:3D655282.5030402 deming-os.org...
 I could see somebody wanting to do something like this:

 template List(X) {
      class List {...};
 };
 template Thingy(A, List(A).List) {...};

 However, since we can't refer to template instances that way - we have
 to instantiate them explictly - I don't see any way to do this with the
 current template syntax.  Do you have any ideas how?

I could see doing that with: template Thingy(A, instance List(A).List) {...};

Why would you need to pass on a completely dependent type as a seperate type parameter? template Thingy(A) { alias ListType instance List(A).List; .... } But how could you write a template with two arguments, one partially dependent on the other? Is this OK? : template AdvancedList(E, F) { ... } template Thingy(A, B : instance AdvancedList(A, C)) { ... } Please correct me if wrong. I was intended to create a template whose second type argument is an instance of AdvancedList, with the restriction that this AdvancedL:ist instance should be instatiated with the "A" type as the first type parameter. I supposed that there is no type named "C". Another thing. Quoting template.html: template Bar(D, D : D[]) { } Is this valid?! You should give unique names to your type parameters. The first type parameter is called "D", and the second? Also I find bewildering the syntax: D : D[] Does this mean that the second type parameter "D" is an array composed of elements of its own type? (Infinite recursion) Yours, Sandor
Aug 26 2002
parent "Walter" <walter digitalmars.com> writes:
"Sandor Hojtsy" <hojtsy index.hu> wrote in message
news:akdcn1$1rs5$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 Why would you need to pass on a completely dependent type as a seperate

 parameter?

As a template specialization of a more general template.
 But how could you write a template with two arguments, one partially
 dependent on the other? Is this OK? :

 template AdvancedList(E, F) { ... }

 template Thingy(A, B : instance AdvancedList(A, C)) { ... }

Yes.
 template Bar(D, D : D[]) { }

 Is this valid?! You should give unique names to your type parameters. The
 first type parameter is called "D", and the second?
 Also I find bewildering the syntax:
 D : D[]
 Does this mean that the second type parameter "D" is an array composed of
 elements of its own type? (Infinite recursion)

Hmm. Perhaps I goofed <g>.
Aug 26 2002
prev sibling next sibling parent reply "Craig Black" <cblack ara.com> writes:
Hmmm .... this "namespace" approach to templates leans toward ease of
implementation, which is good.

Is there any way to do non-type template parameters?

template VectorNamespace(T, int S)
{
    class Vector
    {
        T[S] array;
    }
}

Also, I think that you should be able to do multiple specialization:

template Foo(T) { ... }
template Foo(T : int, float, double) { ... }

-Craig
Aug 21 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Craig Black" <cblack ara.com> wrote in message
news:ak0h9n$1p37$1 digitaldaemon.com...
 Hmmm .... this "namespace" approach to templates leans toward ease of
 implementation, which is good.

It's a bit of an adjustment, but in the end I think it is superior.
 Is there any way to do non-type template parameters?

 template VectorNamespace(T, int S)
 {
     class Vector
     {
         T[S] array;
     }
 }

Not directly. It can be done indirectly as part of a type, or as a class member where the class is passed as a type parameter. I thought a lot about this, and decided that non-type parameters were rare, but added a lot of complexity to the parameters, so they weren't worth supporting directly.
 Also, I think that you should be able to do multiple specialization:

 template Foo(T) { ... }
 template Foo(T : int, float, double) { ... }

That can be faked using two templates, one instantiating the other.
Aug 21 2002
parent reply Mac Reiter <Mac_member pathlink.com> writes:
 Is there any way to do non-type template parameters?

 template VectorNamespace(T, int S)
 {
     class Vector
     {
         T[S] array;
     }
 }

Not directly. It can be done indirectly as part of a type, or as a class member where the class is passed as a type parameter. I thought a lot about this, and decided that non-type parameters were rare, but added a lot of complexity to the parameters, so they weren't worth supporting directly.
 Also, I think that you should be able to do multiple specialization:

 template Foo(T) { ... }
 template Foo(T : int, float, double) { ... }

That can be faked using two templates, one instantiating the other.

It is entirely possible that my current QWERTY -> DVORAK immersion retraining is just consuming too much of my mental flexibility quotient, but I suspect that some examples may be necessary to illustrate these solutions. This could go on a page similar to your "Converting C to D" and "Converting C++ to D", where you could show the C++ methodology and then the corresponding D methodology. Mac
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak0m6l$2jt5$1 digitaldaemon.com...
 It is entirely possible that my current QWERTY -> DVORAK immersion

 just consuming too much of my mental flexibility quotient, but I suspect

 some examples may be necessary to illustrate these solutions.  This could

 a page similar to your "Converting C to D" and "Converting C++ to D",

 could show the C++ methodology and then the corresponding D methodology.

You're quite right.
Aug 21 2002
prev sibling parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
It would also be real nice if version() statements inside the template could
access the supplied actual template parameters.

template Foo(T)
{
  version(T.class.string == "int")
  {
     // do int thingy
  }
  else
  {
    // do generic thingy
  }
}

This can be simpler than specialization and is something I sorely miss in
C++.

Sean

"Craig Black" <cblack ara.com> wrote in message
news:ak0h9n$1p37$1 digitaldaemon.com...
 Hmmm .... this "namespace" approach to templates leans toward ease of
 implementation, which is good.

 Is there any way to do non-type template parameters?

 template VectorNamespace(T, int S)
 {
     class Vector
     {
         T[S] array;
     }
 }

 Also, I think that you should be able to do multiple specialization:

 template Foo(T) { ... }
 template Foo(T : int, float, double) { ... }

 -Craig

Aug 22 2002
parent "Walter" <walter digitalmars.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:ak36kl$23os$1 digitaldaemon.com...
 It would also be real nice if version() statements inside the template

 access the supplied actual template parameters.

 template Foo(T)
 {
   version(T.class.string == "int")
   {
      // do int thingy
   }
   else
   {
     // do generic thingy
   }
 }

 This can be simpler than specialization and is something I sorely miss in
 C++.

Yes, I've been thinking about that. It is a good idea, but there are many ways to do it.
Aug 23 2002
prev sibling next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Wed=2C 21 Aug 2002 00=3A56=3A13 -0700 =22Walter=22
=3Cwalter=40digitalmars=2Ecom=3E wrote=3A

=3E www=2Edigitalmars=2Ecom=2Ftemplate=2Ehtml
=3E 
=3E Notice how short it is =3Cg=3E=2E Ok=2C what did I miss=3F

It's okay=2C but I think it could provide some more syntactic sugar=2E
It would be much better if we don't need to write a separate instantiation
for every template used in the program=2E Some in-place syntax would
be better=2E Something like this=3A

=09template Container=28T=29
=09{
=09=09class Stack
=09=09{
=09=09=09private T=5B=5D pile=3B
=09=09=09
=09=09=09void push=28T x=29
=09=09=09{=09
=09=09=09=09pile ~=3D x=3B
=09=09=09}

=09=09=09T pop=28=29
=09=09=09{
=09=09=09=09T x =3D pile=5Bpile=2Elength - 1=5D=3B
=09=09=09=09pile=2Elength =3D pile=2Elength - 1=3B
=09=09=09=09return x=3B
=09=09=09}
=09=09}
=09}

=09Container=28int=29=2EStack s=3B

This is to avoid cluttering the namespace with continuous lists of
instantiations =28which is going to happen with any large program
using templates extensively=29=2E
Aug 21 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
I hear you, but I want to try it with explicit instantiation names first.
The reason is because I find templated code with lots of implicit
instantiations to be totally unreadable.


"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374899452836458 news.digitalmars.com...
Container(int).Stack s;
This is to avoid cluttering the namespace with continuous lists of
instantiations (which is going to happen with any large program
using templates extensively).
Aug 21 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
It's still explicit, it just isn't a declaration by itself, it's part of
another declaration or even part of a statement.  Legal wherever a typespec
or field reference is legal.

Sean

"Walter" <walter digitalmars.com> wrote in message
news:ak0qju$37o$1 digitaldaemon.com...
 I hear you, but I want to try it with explicit instantiation names first.
 The reason is because I find templated code with lots of implicit
 instantiations to be totally unreadable.


 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:CFN374899452836458 news.digitalmars.com...
 Container(int).Stack s;
 This is to avoid cluttering the namespace with continuous lists of
 instantiations (which is going to happen with any large program
 using templates extensively).

Aug 22 2002
parent reply "Walter" <walter digitalmars.com> writes:
I think that can work if it is prefaced by the keyword "instance".

"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:ak36qj$23r4$1 digitaldaemon.com...
 It's still explicit, it just isn't a declaration by itself, it's part of
 another declaration or even part of a statement.  Legal wherever a

 or field reference is legal.

 Sean

 "Walter" <walter digitalmars.com> wrote in message
 news:ak0qju$37o$1 digitaldaemon.com...
 I hear you, but I want to try it with explicit instantiation names


 The reason is because I find templated code with lots of implicit
 instantiations to be totally unreadable.


 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:CFN374899452836458 news.digitalmars.com...
 Container(int).Stack s;
 This is to avoid cluttering the namespace with continuous lists of
 instantiations (which is going to happen with any large program
 using templates extensively).


Aug 23 2002
parent reply Pavel Minayev <evilone omen.ru> writes:
On Fri=2C 23 Aug 2002 20=3A40=3A53 -0700 =22Walter=22
=3Cwalter=40digitalmars=2Ecom=3E wrote=3A

=3E I think that can work if it is prefaced by the keyword =22instance=22=2E

Great! So=2C we could write=3F

=09instance Container=28double=29=2Edeque q=3B
Aug 24 2002
parent "Walter" <walter digitalmars.com> writes:
Yes.

"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374924726702083 news.digitalmars.com...
On Fri, 23 Aug 2002 20:40:53 -0700 "Walter" <walter digitalmars.com> wrote:

 I think that can work if it is prefaced by the keyword "instance".

Great! So, we could write? instance Container(double).deque q;
Aug 24 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374899452836458 news.digitalmars.com...
Container(int).Stack s;
This is to avoid cluttering the namespace with continuous lists of
instantiations (which is going to happen with any large program
using templates extensively).

I forgot to mention that has syntax parsing problems, too. How about this: instance Container(int).Stack s; ?
Aug 21 2002
next sibling parent Joel Lucsy <jjlucsy ameritech.net> writes:
"Walter" <walter digitalmars.com> wrote in
news:ak0t51$k2o$1 digitaldaemon.com: 

 
 "Pavel Minayev" <evilone omen.ru> wrote in message
 news:CFN374899452836458 news.digitalmars.com...
Container(int).Stack s;
This is to avoid cluttering the namespace with continuous lists of
instantiations (which is going to happen with any large program
using templates extensively).

I forgot to mention that has syntax parsing problems, too. How about this: instance Container(int).Stack s; ?

Just to throw it out: Stack s from instance of Container(int); or: Stack s from instance Container(int); or: Stack s instance Container(int); or: Stack s in Container(int);
Aug 21 2002
prev sibling parent Pavel Minayev <evilone omen.ru> writes:
On Wed, 21 Aug 2002 13:10:47 -0700 "Walter" <walter digitalmars.com> wrote:

 I forgot to mention that has syntax parsing problems, too. How about this:
 
 instance Container(int).Stack s;
 
 ?

This is also fine!
Aug 21 2002
prev sibling next sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
One possible problem with the static but multiply nameable nature of templates
is that person A working on Foo.d might instantiate (to pick a template name at
semi-random) Container(int) as FooCont.  Person B might make Container(int) as
BarCont in Bar.d.  If the Container template has any variables in it, they would
be somewhat like global variables.  The problem is, A would think they had their
own copy in FooCont and B would think theirs was safe in BarCont.

Because of the description, *if* they know about each other, then they will know
that these global variables are not "safe".  But a Container(int) is (for sake
of argument) a common thing to make, and anyone could make one at any time.  You
most likely would not know that someone, perhaps a library writer, had already
instantiated Container(int).  Since you can't ever know who else might be
changing the variables inside your template instance, you can't really ever
safely use those variables.  If you are single threaded, you arguably could use
the variables as long as you never did anything that would cause code outside of
your control to execute, but that becomes more and more difficult as code
becomes more object oriented.

What problems would be caused by having each "equivalently parameterized"
instance of a template manage their own variables?  The
classes/structs/enums/etc could still be shared, so that person A could pass a
reference to a FooCont.SomeClass object into person B's code, where it is used
as a BarCont.SomeClass object.  Of course, if you do that, the FooCont.SomeClass
object may be surprised when values of the Container(int) variables change when
it moves into BarCont territory...

Maybe template variables should just be outlawed?  "const"s are fine, since
nobody can change them, and they are extremely useful for configuration options.
But variables just seem to be dangerous and seem to risk violating encapsulation
expectations.

Mac
Aug 21 2002
parent "Walter" <walter digitalmars.com> writes:
The problem only occurs with template variables. It's really the same issue
as global variables in general, they have their uses but should normally be
avoided. I can see cases where they would be useful, for example, to hold a
handle to a globally shared resource.

"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak0pde$2s5j$1 digitaldaemon.com...
 Maybe template variables should just be outlawed?  "const"s are fine,

 nobody can change them, and they are extremely useful for configuration

 But variables just seem to be dangerous and seem to risk violating

 expectations.

Aug 21 2002
prev sibling next sibling parent reply Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in news:ajvgsb$6gt$1
 digitaldaemon.com:

 www.digitalmars.com/template.html
 
 Notice how short it is <g>. Ok, what did I miss?
 
 

Templates. Cool! Does the statement "Semantic analysis is not done until instantiated." mean that the tempate will pick up variables and functions in the scope in which it is instantiated? // File: temp.d template Foo(T) { bit Bar(T a) { return a < BorgConst; } } // File alpha.d import temp; int BorgConst = 12; in Foo(int) here; here.Bar(5); // returns true If this is true it would seem to perhaps be at odds with this statement "Multiple instantiations of a TemplateDeclaration with the same TemplateParameterList all will refer to the same instantiation." Files alpha.d and bravo.d are in the same project. // File bravo.d int BorgConst = 4; in Foo(int) here; here.Bar(5); // returns false ?
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Patrick Down" <pat codemoon.com> wrote in message
news:Xns9271999977E06patcodemooncom 63.105.9.61...
 Does the statement "Semantic analysis is not done until
 instantiated." mean that the tempate will pick up variables
 and functions in the scope in which it is instantiated?

Nope. The only thing it will pick up from the instantiation scope is the template argument types.
 // File: temp.d
 template Foo(T)
 {
   bit Bar(T a)
   {
     return a < BorgConst;
   }
 }

 // File alpha.d
 import temp;

 int BorgConst = 12;

 in Foo(int) here;

 here.Bar(5); // returns true

 If this is true it would seem to perhaps be at odds with
 this statement "Multiple instantiations of a TemplateDeclaration
 with the same TemplateParameterList all will refer to the same
 instantiation."

Correct, which is one reason why it is false (and your example will fail to compile with a "BorgConst undefined" error). Templates are instantiated in the scope in which the template is declared. This avoids the whole C++ mess of "non-dependent names" versus "dependent names" and their subtly different lookup schemes. Clean separation between syntax and semantics is what makes this easy to do in D, and nearly impossible in C++.
Aug 21 2002
parent reply Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in
news:ak0uas$rqn$1 digitaldaemon.com: 

 
 "Patrick Down" <pat codemoon.com> wrote in message
 news:Xns9271999977E06patcodemooncom 63.105.9.61...
 Does the statement "Semantic analysis is not done until
 instantiated." mean that the tempate will pick up variables
 and functions in the scope in which it is instantiated?

Nope. The only thing it will pick up from the instantiation scope is the template argument types.

Have you see C++ template usage like this? template<class T> class Foo { public: int addToClassFunc(int param) { T* pT = static_cast<T*>(this); return pT->classFunc() + param; } } class Bar : public A, public Foo<Bar> { int classFunc() { return 12; } }; Bar b; b.addToClassFunc(5); // return 15 Of course D doesn't have multiple inheritance. But do you really need it to be able to do functionality aggregation like the C++ example above?
Aug 22 2002
next sibling parent reply Patrick Down <pat codemoon.com> writes:
What about this...

template Foo(this : T) // use this to bind it to a class
{
  int addClassFunc(int a)
  {
    return this.localFunc() + a;
  }
}

class Bar
{
  in Foo(Bar) mixin;

  int localFunc()
  {
    return 12;
  }
}
Aug 22 2002
parent "Walter" <walter digitalmars.com> writes:
Offhand I don't see why that shouldn't work. -Walter

"Patrick Down" <pat codemoon.com> wrote in message
news:Xns927264FD45DF2patcodemooncom 63.105.9.61...
 What about this...

 template Foo(this : T) // use this to bind it to a class
 {
   int addClassFunc(int a)
   {
     return this.localFunc() + a;
   }
 }

 class Bar
 {
   in Foo(Bar) mixin;

   int localFunc()
   {
     return 12;
   }
 }

Aug 22 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
No, I haven't. But perhaps it can be done with interfaces.

"Patrick Down" <pat codemoon.com> wrote in message
news:Xns927255B703EE3patcodemooncom 63.105.9.61...
 Have you see C++ template usage like this?

 template<class T>
 class Foo
 {
   public:
   int addToClassFunc(int param)
   {
     T* pT = static_cast<T*>(this);

     return pT->classFunc() + param;
   }
 }

 class Bar : public A, public Foo<Bar>
 {
   int classFunc()
   {
     return 12;
   }
 };

 Bar b;
 b.addToClassFunc(5); // return 15


 Of course D doesn't have multiple inheritance.
 But do you really need it to be able to do
 functionality aggregation like the C++ example
 above?

Aug 22 2002
parent Patrick Down <pat codemoon.com> writes:
"Walter" <walter digitalmars.com> wrote in news:ak2umn$1qcd$1
 digitaldaemon.com:

 No, I haven't. But perhaps it can be done with interfaces.
 

It's used extensivly by microsoft in ATL. It's one of those C++ this where I find the C++ template syntax bad, but the general idea has merit.
Aug 22 2002
prev sibling next sibling parent reply "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html

 Notice how short it is <g>. Ok, what did I miss?

I would like the ability to pass constants such as literals and function pointers. For example, a smart-pointer could have a special free function given as argument. A feature I have been missing in C++, is the ability to have conditional compilation based on the concrete types. This would be useful if eg. 95% of the template code is the same, but the rest needs to be different. In some cases, it would also allow the template to provide a more efficient implementation for a specific type. Regards, Martin M. Pedersen
Aug 21 2002
next sibling parent reply "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ak0udv$sch$1 digitaldaemon.com...
 For example, a smart-pointer could have a special free function > given as

It reminds me: Smart pointers would need the ability to redefine the member operator. Is it feasible in D? Regards, Martin M. Pedersen
Aug 21 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ak108p$18il$1 digitaldaemon.com...
 It reminds me: Smart pointers would need the ability to redefine the

 operator. Is it feasible in D?

In a garbage collected language, is there a need for smart pointers?
Aug 21 2002
next sibling parent "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Walter" <walter digitalmars.com> wrote in message
news:ak147n$273f$1 digitaldaemon.com...
 In a garbage collected language, is there a need for smart pointers?

I'm not sure, considering try/catch/finally. But there are lots of C APIs out there returning POD structures with special free functions. fopen()/fclose() and opendir()/closedir() are simple examples of this, MAPI with MAPIFreeBuffer() is another. Even if D itself is garbage collected, many applications written in D will use such APIs, and GC will not solve all their problems. If I was to use the GC to clean up, I would need something like: interface Free { void special_free(void *); } template TSmartPointer(T, F : Free) { class SmartPointer { this(T* t) { m_t = t; } ~this() { F.special_free(m_t); } T* m_t; } } class CloseFile: Free { void special_free(void* fp) { fclose((FILE*)fp); } // I don't check the result, I know - slap me. } instance TSmartPointer fileClosing(FILE, CloseFile); FILE* fp = fopen("name", "r"); fileClosing.SmartPointer closeÍt = new fileClosing.SmartPointer(fp); ... now access it using fp Clearly, that is too tedious. try/finally would probably be a better solution... Regards, Martin M. Pedersen
Aug 21 2002
prev sibling parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak147n$273f$1 digitaldaemon.com...

 In a garbage collected language, is there a need for smart pointers?

Butofcourse. Smart pointers and memory management don't necessarily have to be related at all. The fact that most smart pointers are only used to manage memory doesn't mean that has to be the case. For example, I have a buncha smart pointers that manage file access. They are "smart" because they will manage the ownership of the file, and "pointers" because they emulate a pointer to an interface with members that perform operations on the file. The way you have D set up, you just can't do this, anyway, which I still think is a real pity and the thing that kills D (alongside with Java) for me. Ahem... Not that I'm not duly impressed... Salutaciones, JCAB
Aug 21 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak1ht1$753$1 digitaldaemon.com...
    The way you have D set up, you just can't do this, anyway, which I

 think is a real pity and the thing that kills D (alongside with Java) for
 me. Ahem... Not that I'm not duly impressed...

Even if you don't use D, I've found your comments about it to be very valuable. Are you sure that all the other neat stuff in D doesn't tempt you? <g>
Aug 21 2002
parent reply "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Walter" <walter digitalmars.com> wrote in message
news:ak1p2n$1ben$1 digitaldaemon.com...

 Even if you don't use D, I've found your comments about it to be very
 valuable.

Thanx.
 Are you sure that all the other neat stuff in D doesn't tempt you?

Yes, it does. But not enough to use it. It does tempt me to try and make my own language, thoguh ;-) Salutaciones, JCAB
Aug 22 2002
parent reply "Sean L. Palmer" <seanpalmer earthlink.net> writes:
I've been thinking along the same lines.  I'd like to see what kind of
language you'd come up with.  I really liked what I saw in Yoritz language,
and NVIDIA Cg.  Something with simplified memory management and designed to
be implemented using SIMD.  I have a lot of vague ideas and no time to
solidify them.

I'm likely to completely ditch C compatibility if I make a language, though.
Too much baggage.  I'd make it able to link with C but not syntactically
compatible.

Sean

"Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> wrote in message
news:ak3ami$286g$1 digitaldaemon.com...
 "Walter" <walter digitalmars.com> wrote in message
 news:ak1p2n$1ben$1 digitaldaemon.com...

 Even if you don't use D, I've found your comments about it to be very
 valuable.

Thanx.
 Are you sure that all the other neat stuff in D doesn't tempt you?

Yes, it does. But not enough to use it. It does tempt me to try and

 my own language, thoguh ;-)

 Salutaciones,
                        JCAB

Aug 23 2002
parent "Juan Carlos Arevalo Baeza" <jcab JCABs-Rumblings.com> writes:
"Sean L. Palmer" <seanpalmer earthlink.net> wrote in message
news:ak5pq2$210p$1 digitaldaemon.com...

 I've been thinking along the same lines.  I'd like to see what kind of
 language you'd come up with.  I really liked what I saw in Yoritz

 and NVIDIA Cg.  Something with simplified memory management and designed

 be implemented using SIMD.  I have a lot of vague ideas and no time to
 solidify them.

:-) Yes, I know the feeling. And I would really like to compare ideas, too. Hmmm... You know what? I think I'm going to start a little discussion list of my own on my server, to discuss this kind of thing. I'm taking the liberty of subscribing you, Sean. Anyone else interested, go here (hope this works) or drop me email: http://lists.jcabs-rumblings.com/listinfo.cgi/npl-jcabs-rumblings.com For short, the language I have in mind is: - Small C-style run-time model and language kernel. This means bit & byte manipulation, basic memory address algebra, variables & functions... With room for platform-specific additions, of course, like I/O port access, etc. - Extremely simple parser (much simpler than even D). This is, I believe, my most outrageous idea. If it works, that is. - Configurable semantics for high extensibility. Ability to create language primitives and sub-languages by programming the mapping of them into the kernel. - Compile-time execution model. - Standard library including higher-level stuff like GC, which would be optional to use.
 I'm likely to completely ditch C compatibility if I make a language,

 Too much baggage.  I'd make it able to link with C but not syntactically
 compatible.

I'm completely ready to ditch compatibility. I'm also working on a C/C++ parser to ease converting source code. Salutaciones, JCAB
Aug 23 2002
prev sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
Minor semantic point, but the more general case of smart resource managers has
begun to be named "smart handles".  Since pointers are inextricably tied up with
memory in so many developers minds, a change in name (while technically
irrelevant) helps us to remember non-memory resources like files, mutexes,
ports, etc.

This has been a major contention with garbage collected systems for one of my
coworkers.  My current stance is that garbage collection solves the 99% rule
with 1% of the effort.  Because D is a pragmatic language, rather than an
academic one, it also supplies two important resource management capabilities:

1. explicit delete
2. finally clauses

Most garbage collected systems refuse to allow *any* way to guarantee when an
object will be freed/finalized.  Explicit deletes at least allow you as much
control as you had in C.  It does not provide the automatic scoping destructor
behavior, however, so you have to use 'finally' clauses.  If you have a resource
that you absolutely have to clear up before you exit a scope, build a finally
clause and clean it up in there.  This does take a bit more effort, but only for
the rarer case of non-memory resources, which you generally end up writing a lot
of exception handling code for anyway.  Non-memory resources are *much* more
likely to be inaccessible, or to exhibit some form of error or other exception
while you are using them, so you have to be careful with them no matter how much
support the language provides.  The addition of a finally clause to a scope that
is already laden with error handling code is not too much of a price to pay, and
actually substantially decreases the programming load from most code that I have
seen.

Having said all of that, it would be nice to have the ability to have a
reference counted object.  I would not want it for all objects, because the
speed penalty of reference counting *all* of your pointers is rather severe
(Nothing like a big heavy book on Garbage Collection to clear out any
misconceptions you have...).  However, for specific things, like files, mutex
locks, ports, etc, it might be nice to have a syntax like:

counted class SerialPort
{
}

This would cause the compiler to add a thread-safe reference counting mechanism
to SerialPort.  Creation, assignment, etc would then have to maintain the
reference count invariant and delete the object as soon as the reference count
hit 0.  It does add special handling to objects of this type, as well as
references/pointers to this type, but I don't think it causes any problems for
separate lexing/parsing.  I think it just requires that the code generator be
able to access the symbol table to know that this object is counted, and the
code generator has to have access to the symbol table anyway...

For basic "allocation is acquisition" style programming, this works great.
Reference comes into being at top of scope, resource is acquired in constructor
and ref count goes up to 1.  At the bottom of the scope, the reference
disappears, the ref count goes down to 0, and the object is deleted, just like
it would have been in C++.  What's really nice is that you can make the thing
inside a function and return it back out.  The process of copying the
pointer/reference into the return value increases the reference count.  The
object will live as long as anyone is using it, and die the instant the last
user lets go.  

Reference counting is notoriously difficult to do in libraries or source code,
because the compiler can perform optimizations around scope boundaries that can
invalidate the reference counting invariant.  If that ever happens, the whole
scheme explodes (you either leak something or free it too soon).  But if the
compiler did the reference counting, it could ensure logically correct reference
counting, even if it took shortcuts for efficiency.  For example, if a local
object was being returned, the compiler knows that one reference existed inside
the function and that one reference will exist outside the function.
Technically, it should increase the reference when it copies the pointer into
the return value, and then decrease the reference when it unrolls the stack and
loses the local variable.  However, it could simply not touch the reference
count at all, and be completely correct in its result.  It would know this
because it was trading 1 local reference for 1 return reference.  A similar
behavior occurs at the point where the return value for the function is assigned
into a variable, and the same optimization can be performed.

By providing a selective ability to use reference counted classes, and by making
it require the extra keyword, D could provide additional flexibility without
adding danger to the new users.  Until you were experienced enough to know what
'counted' was for and why you would want to use it, you would simply leave all
your classes GC'ed, which is logically correct, even if it might introduce
longer pauses before resources were freed.  You *don't* want to go to all
reference counted classes, because the speed penalties are bad, and more
importantly, because reference counting cannot remove cyclic systems.

A.next = B;
B.next = C;
C.next = B;  // made a loop with B and C
// do something
A.next = NULL;
// No way to reach B or C, but each still has a reference count of 1
// because of the loop.

Mark and sweep, which D currently uses, has absolutely no problem whatsoever
with cyclic systems, and (depending on the implementation of the algorithm) can
provide more tunable memory management CPU consumption than reference counting.
That makes GC a much better general purpose solution than refcounting.

Sorry, that was more rambling than I intended, and probably more than this
audience needed...
Mac

In article <ak1ht1$753$1 digitaldaemon.com>, Juan Carlos Arevalo Baeza says...
"Walter" <walter digitalmars.com> wrote in message
news:ak147n$273f$1 digitaldaemon.com...

 In a garbage collected language, is there a need for smart pointers?

Butofcourse. Smart pointers and memory management don't necessarily have to be related at all. The fact that most smart pointers are only used to manage memory doesn't mean that has to be the case. For example, I have a buncha smart pointers that manage file access. They are "smart" because they will manage the ownership of the file, and "pointers" because they emulate a pointer to an interface with members that perform operations on the file. The way you have D set up, you just can't do this, anyway, which I still think is a real pity and the thing that kills D (alongside with Java) for me. Ahem... Not that I'm not duly impressed... Salutaciones, JCAB

Aug 22 2002
next sibling parent reply "Walter" <walter digitalmars.com> writes:
Your comments on smart handles in D explain it better than I have.
Mind if I plagairize it a bit?

I also
suspect you are right in that the only way to do reference counting right is
to clue the compiler in and have the compiler manage it.
Aug 22 2002
parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <ak32sg$1v7v$1 digitaldaemon.com>, Walter says...
Your comments on smart handles in D explain it better than I have.
Mind if I plagairize it a bit?

I would be honored.
I also
suspect you are right in that the only way to do reference counting right is
to clue the compiler in and have the compiler manage it.

I know that 'counted' would increase the complexity of the compiler, which you have been trying to avoid, but I think it could be done without too much additional effort. (I am not, however, familiar with writing compilers, so my assumptions could be vastly incorrect) It would certainly take less effort to get it right (ignoring issues of "completely optimized" for the time being) inside the compiler than it does outside. Mac
Aug 22 2002
parent reply "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak3agl$27qt$1 digitaldaemon.com...
 I know that 'counted' would increase the complexity of the compiler, which

 have been trying to avoid, but I think it could be done without too much
 additional effort.  (I am not, however, familiar with writing compilers,

 assumptions could be vastly incorrect)  It would certainly take less

 get it right (ignoring issues of "completely optimized" for the time

 inside the compiler than it does outside.

The complexity comes from decrementing the counts of objects going out of scope, in the presence of exceptions. It did occur to me that if this was supported, it would also implicitly support the people who want deterministic destruction.
Aug 23 2002
parent reply Mac Reiter <Mac_member pathlink.com> writes:
In article <ak5rgs$23m5$1 digitaldaemon.com>, Walter says...
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak3agl$27qt$1 digitaldaemon.com...
 I know that 'counted' would increase the complexity of the compiler, which


The complexity comes from decrementing the counts of objects going out of
scope, in the presence of exceptions. It did occur to me that if this was
supported, it would also implicitly support the people who want
deterministic destruction.

Yup. Actually, I originally started thinking about a 'scoped' keyword whose sole purpose was to supply scoped destruction, but then I got to thinking about the times when you might want to make a new thing and pass it out of a function, but you still wanted its resources cleaned up as quickly as possible. So I started thinking about reference counting and realized that it would handle the 'scoped' behavior as a trivial case. Made me happy... Mac
Aug 23 2002
parent "Walter" <walter digitalmars.com> writes:
"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak5v0i$277b$1 digitaldaemon.com...
 Yup.  Actually, I originally started thinking about a 'scoped' keyword

 sole purpose was to supply scoped destruction, but then I got to thinking

 the times when you might want to make a new thing and pass it out of a

 but you still wanted its resources cleaned up as quickly as possible.  So

 started thinking about reference counting and realized that it would

 'scoped' behavior as a trivial case.  Made me happy...

It's pretty cool when solving one problem provides a trivial solution to an apparently unrelated problem.
Aug 23 2002
prev sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Thu, 22 Aug 2002 15:29:07 +0000 (UTC) Mac Reiter <Mac_member pathlink.com> 
wrote:

 A.next = B;
 B.next = C;
 C.next = B;  // made a loop with B and C
 // do something
 A.next = NULL;
 // No way to reach B or C, but each still has a reference count of 1
 // because of the loop.

Note that if GC will still run on refcounted objects as well, it provides a solution (or sort of) - such cases will get resolved (and destructors will be called), but not immediately...
Aug 22 2002
parent Mac Reiter <Mac_member pathlink.com> writes:
Thank you.  I meant to illustrate reference counting as a means of minimizing
reclamation latency, but relying on garbage collection as the final means of
ensuring correctness/completeness of de-allocation.  Somewhere along the way I
forgot to state that clearly...  Guess I ramble too much.  

I just wanted to make sure that nobody thought "Hey, reference counting gives
quick reclamation!  Why don't we just use that instead of this garbage
collection system?"  I prefer the combination so that you can take advantages of
the strengths of each system.

Mac

In article <CFN374909677284259 news.digitalmars.com>, Pavel Minayev says...
On Thu, 22 Aug 2002 15:29:07 +0000 (UTC) Mac Reiter <Mac_member pathlink.com> 
wrote:

 A.next = B;
 B.next = C;
 C.next = B;  // made a loop with B and C
 // do something
 A.next = NULL;
 // No way to reach B or C, but each still has a reference count of 1
 // because of the loop.

Note that if GC will still run on refcounted objects as well, it provides a solution (or sort of) - such cases will get resolved (and destructors will be called), but not immediately...

Aug 22 2002
prev sibling parent reply "Walter" <walter digitalmars.com> writes:
"Martin M. Pedersen" <mmp www.moeller-pedersen.dk> wrote in message
news:ak0udv$sch$1 digitaldaemon.com...
 I would like the ability to pass constants such as literals and function
 pointers. For example, a smart-pointer could have a special free function
 given as argument.

I've thought about this. Wouldn't it make sense to have an argument that is an interface, the implementation of which has the desired special free function? interface Free { void special_free(void *); } template Foo(T, U : Free) { ... U.special_free(p); ... } class Bar : Free { void special_free(void *p) { ... } } instance Foo(int, Bar) abc;
 A feature I have been missing in C++, is the ability to have conditional
 compilation based on the concrete types. This would be useful if eg. 95%

 the template code is the same, but the rest needs to be different. In some
 cases, it would also allow the template to provide a more efficient
 implementation for a specific type.

This would be a valuable feature, but there are many different ways to do it. I haven't gone far down the path of exploring them.
Aug 21 2002
parent "Martin M. Pedersen" <mmp www.moeller-pedersen.dk> writes:
Hi,

"Walter" <walter digitalmars.com> wrote in message
news:ak119a$1ga1$1 digitaldaemon.com...
 I've thought about this. Wouldn't it make sense to have an argument that

 an interface, the implementation of which has the desired special free
 function?

That would do it, I think :-)
 A feature I have been missing in C++, is the ability to have conditional
 compilation based on the concrete types.

it. I haven't gone far down the path of exploring them.

My solution to the missing feature in C++, has sometimes been to use overloaded functions outside the template, but I have also resorted to not using a template at all, but auto-generate classes instead by some special preprocessing. Regards, Martin M. Pedersen
Aug 21 2002
prev sibling parent reply "anderson" <anderson firestar.com.au> writes:
Wow, one day since this was posted and all these emails.

I was wondering about this syntax sugar.

    //Where foo is an template
    import foo(int);

Then you could use all the methods like C functions.

    x = Max(a,b);

Ofcourse problems would occur when there are more then one...

    import foo(int);
    import foo(float);

But the complier could work that out.

"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html

 Notice how short it is <g>. Ok, what did I miss?

Aug 22 2002
next sibling parent reply Pavel Minayev <evilone omen.ru> writes:
On Thu, 22 Aug 2002 18:45:44 +0800 "anderson" <anderson firestar.com.au> wrote:

 I was wondering about this syntax sugar.
 
     //Where foo is an template
     import foo(int);
 
 Then you could use all the methods like C functions.
 
     x = Max(a,b);
 
 Ofcourse problems would occur when there are more then one...
 
     import foo(int);
     import foo(float);
 
 But the complier could work that out.

Yes, just as if the function was overloaded (and any clashes like foo(int) declared twice are considered errors). I like this!
Aug 22 2002
parent "anderson" <anderson firestar.com.au> writes:
I though of another extention to that idea. How about if there is a
collision (overload), the user would have to do this...

import max(int)
import max(float)

int.max(a,b);
float.max(x,y);

That way it's simular to how D handles other overloading.

PS
I haven't been able to use this newgroup for a while, my computer crashed
and I've spent several days repairing it. Darn XP.


"Pavel Minayev" <evilone omen.ru> wrote in message
news:CFN374907807516898 news.digitalmars.com...
 On Thu, 22 Aug 2002 18:45:44 +0800 "anderson" <anderson firestar.com.au>

 I was wondering about this syntax sugar.

     //Where foo is an template
     import foo(int);

 Then you could use all the methods like C functions.

     x = Max(a,b);

 Ofcourse problems would occur when there are more then one...

     import foo(int);
     import foo(float);

 But the complier could work that out.

Yes, just as if the function was overloaded (and any clashes like foo(int) declared twice are considered errors). I like this!

Aug 27 2002
prev sibling parent reply Mac Reiter <Mac_member pathlink.com> writes:
(The following message represents me thinking and typing at the same time.  It
thus flows along my stream of consciousness, and my opinions change as it
progresses.  Please read the entire post before replying...)

Since 'import' already has a meaning, and since D templates do share many
features in common with namespaces, would 'using' be a better keyword?

using foo(int);

or, to be more complete and provide more flexibility:

using template foo(int);  // moves all template members into current namespace
using foo(int).Max;       // only moves Max into current namespace

If you attempted to bring in entire multiple instantiations of the same
template, that could probably just be outlawed.  Since you would have to do the
equivalent of a scope resolution at every usage to tell the compiler what to do
(since *all* of the names would have collided), you might as well have just done
that and skipped the 'using' step.

If you brought in different pieces of the same template, but from different
instantiations:

using foo(int).Max;
using foo(double).Min;

then there would be no namespace collision, and it might be allowable.  Horribly
confusing, in the example given above, but possibly useful in some situations...

Overall, I think that the 'using template foo(int);' form would be a handy
addition for people who only needed a single instantiation of the template and
didn't want to have to precede every template member usage with 'intfoo.'.
Trying to extend the usage beyond that, however, causes more problems than it
solves.

Which leads me to my favorite solution so far, and it is even already in the
language (at least in the syntax, even if the template usage of it may not be in
the compiler yet) -- 'with'!

with foo(int)
{
x = Max(a,b);
// more stuff, obviously
}

or maybe:

with inst foo(int)
{
}

or:

inst foo(int) intfoo;
with intfoo
{
}

This saves adding another template specific keyword, explains how name
collisions will be resolved (innermost 'with' clause that contains such a member
will win), and just generally seems nice and clean to me...  That may be my
Visual Basic experience coming back to me (VB also uses 'with', and you use it a
LOT when you start getting OOPed in VB).

Mac

In article <ak2f15$179g$1 digitaldaemon.com>, anderson says...
Wow, one day since this was posted and all these emails.

I was wondering about this syntax sugar.

    //Where foo is an template
    import foo(int);

Then you could use all the methods like C functions.

    x = Max(a,b);

Ofcourse problems would occur when there are more then one...

    import foo(int);
    import foo(float);

But the complier could work that out.

"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html

 Notice how short it is <g>. Ok, what did I miss?


Aug 22 2002
next sibling parent "Walter" <walter digitalmars.com> writes:
I hadn't thought of using the with, but it just might be the ticket. And no,
it doesn't work yet with templates because templates aren't implemented at
all :-( in the compiler.

"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak2tg7$1p28$1 digitaldaemon.com...
 (The following message represents me thinking and typing at the same time.

 thus flows along my stream of consciousness, and my opinions change as it
 progresses.  Please read the entire post before replying...)

 Since 'import' already has a meaning, and since D templates do share many
 features in common with namespaces, would 'using' be a better keyword?

 using foo(int);

 or, to be more complete and provide more flexibility:

 using template foo(int);  // moves all template members into current

 using foo(int).Max;       // only moves Max into current namespace

 If you attempted to bring in entire multiple instantiations of the same
 template, that could probably just be outlawed.  Since you would have to

 equivalent of a scope resolution at every usage to tell the compiler what

 (since *all* of the names would have collided), you might as well have

 that and skipped the 'using' step.

 If you brought in different pieces of the same template, but from

 instantiations:

 using foo(int).Max;
 using foo(double).Min;

 then there would be no namespace collision, and it might be allowable.

 confusing, in the example given above, but possibly useful in some

 Overall, I think that the 'using template foo(int);' form would be a handy
 addition for people who only needed a single instantiation of the template

 didn't want to have to precede every template member usage with 'intfoo.'.
 Trying to extend the usage beyond that, however, causes more problems than

 solves.

 Which leads me to my favorite solution so far, and it is even already in

 language (at least in the syntax, even if the template usage of it may not

 the compiler yet) -- 'with'!

 with foo(int)
 {
 x = Max(a,b);
 // more stuff, obviously
 }

 or maybe:

 with inst foo(int)
 {
 }

 or:

 inst foo(int) intfoo;
 with intfoo
 {
 }

 This saves adding another template specific keyword, explains how name
 collisions will be resolved (innermost 'with' clause that contains such a

 will win), and just generally seems nice and clean to me...  That may be

 Visual Basic experience coming back to me (VB also uses 'with', and you

 LOT when you start getting OOPed in VB).

 Mac

Aug 22 2002
prev sibling next sibling parent Pavel Minayev <evilone omen.ru> writes:
On Thu=2C 22 Aug 2002 14=3A46=3A31 +0000 =28UTC=29 Mac Reiter
=3CMac=5Fmember=40pathlink=2Ecom=3E 
wrote=3A

=3E If you attempted to bring in entire multiple instantiations of the same
=3E template=2C that could probably just be outlawed=2E  Since you would have
to do 
the
=3E equivalent of a scope resolution at every usage to tell the compiler what
to 
do
=3E =28since *all* of the names would have collided=29=2C you might as well
have just 
done
=3E that and skipped the 'using' step=2E

I don't think it is an error=2E=2E=2E you should just get two versions of the
same 
function=2C just
if it was overloaded=3A

=09using Min=28int=29=3B
=09using Min=28double=29=3B

=09int a=2C b=3B
=09double c=2C d=3B
=09x =3D min=28a=2C b=29=3B=09=2F=2F Min=28int=29=2Emin
=09y =3D min=28c=2C d=29=3B=09=2F=2F Min=28double=29=2Emin
Aug 22 2002
prev sibling parent "anderson" <anderson firestar.com.au> writes:
Sounds good

"Mac Reiter" <Mac_member pathlink.com> wrote in message
news:ak2tg7$1p28$1 digitaldaemon.com...
 (The following message represents me thinking and typing at the same time.

 thus flows along my stream of consciousness, and my opinions change as it
 progresses.  Please read the entire post before replying...)

 Since 'import' already has a meaning, and since D templates do share many
 features in common with namespaces, would 'using' be a better keyword?

 using foo(int);

 or, to be more complete and provide more flexibility:

 using template foo(int);  // moves all template members into current

 using foo(int).Max;       // only moves Max into current namespace

 If you attempted to bring in entire multiple instantiations of the same
 template, that could probably just be outlawed.  Since you would have to

 equivalent of a scope resolution at every usage to tell the compiler what

 (since *all* of the names would have collided), you might as well have

 that and skipped the 'using' step.

 If you brought in different pieces of the same template, but from

 instantiations:

 using foo(int).Max;
 using foo(double).Min;

 then there would be no namespace collision, and it might be allowable.

 confusing, in the example given above, but possibly useful in some

 Overall, I think that the 'using template foo(int);' form would be a handy
 addition for people who only needed a single instantiation of the template

 didn't want to have to precede every template member usage with 'intfoo.'.
 Trying to extend the usage beyond that, however, causes more problems than

 solves.

 Which leads me to my favorite solution so far, and it is even already in

 language (at least in the syntax, even if the template usage of it may not

 the compiler yet) -- 'with'!

 with foo(int)
 {
 x = Max(a,b);
 // more stuff, obviously
 }

 or maybe:

 with inst foo(int)
 {
 }

 or:

 inst foo(int) intfoo;
 with intfoo
 {
 }

 This saves adding another template specific keyword, explains how name
 collisions will be resolved (innermost 'with' clause that contains such a

 will win), and just generally seems nice and clean to me...  That may be

 Visual Basic experience coming back to me (VB also uses 'with', and you

 LOT when you start getting OOPed in VB).

 Mac

 In article <ak2f15$179g$1 digitaldaemon.com>, anderson says...
Wow, one day since this was posted and all these emails.

I was wondering about this syntax sugar.

    //Where foo is an template
    import foo(int);

Then you could use all the methods like C functions.

    x = Max(a,b);

Ofcourse problems would occur when there are more then one...

    import foo(int);
    import foo(float);

But the complier could work that out.

"Walter" <walter digitalmars.com> wrote in message
news:ajvgsb$6gt$1 digitaldaemon.com...
 www.digitalmars.com/template.html

 Notice how short it is <g>. Ok, what did I miss?



Aug 28 2002