Topic: Better template syntax?


Author: willer@carolian.com (Steve Willer)
Date: 1996/04/22
Raw View
Roman Lechtchinsky <wolfro@cs.tu-berlin.de> wrote:

>Actually, when I define a template I usually describe what it expects from
>its parameters in a comment. This means I have to write this list anyway. Do
>other programmers do it?

Just a quick note: I don't do that. All I expect is that programmers write
reasonable classes based on well-known rules (like: If your class needs a
specialized copy constructor, destructor or operator=, then it needs all
three). If my code expects certain things of the classes given to it (like an
operator== or whatever), then the code won't compile.

Of course, I'm not writing a library for external use -- this is all internal
company stuff. However, it still doesn't seem worth it to document this
"requirements" list, especially since it adds to maintenance requirements and
is potentially inaccurate.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/04/17
Raw View
Matt Austern wrote:
>
> In article <31741E6C.53CA@cs.tu-berlin.de> Roman Lechtchinsky
> <wolfro@cs.tu-berlin.de> writes:
>
> > sorry for being curious but I'm only human... My question is: when declaring
> > templates, why isn't it required to declare what one expects of the
> > template's parameters? Let's create a template function:
> >
> > template<class T> bool equal( const T& t1, const T& t2 )
> > {
> >   return t1==t2;
> > }
> >
> > Now, personally I would prefer to write something like:
> >
> > template<class T { bool operator==( const T& t ) const; }> ...
>
> Specifying something like this correctly turns out to be extremely
> difficult: it's very hard to come up with syntax that neither
> overspecifies nor underspecifies the constraints.  In this particular
> case, for example: do you really care whether operator== takes a const
> T&, as opposed to a T?

Sure I do. If it takes a T the template needs access to the copy constructor.
This is something one tends to forget about - the compiler wouldn't let you
if you had to declare this fact.

> Does it matter that it returns bool, instead
> of something (like int) that is freely convertable to bool?

It depends on what this something is and whether you really want it to be
converted to bool or not ( in the case of operator== you almost certainly do;
maybe it's a bad example ). No problem if it is really int. If it is a class
which can be converted to bool or to int or even to a pointer then the
programmer should at least know which conversions might occur. Usually
templates are not documented that good.
As a matter of fact, my definition could accept an operator== with a
different return type. When calling it the return value would be converted to
bool prior to any other conversions which would give the programmer at least
some control.

> Does it
> matter whether this is T::operator==(T), or ::operator==(T,T), or any
> of a dozen other minor variations?

Note that this problem occurs only with operators. Again, the two operators
you mention have different semantics: the second one copies two objects
whereas the first one copies only one.

>
> More realistic cases are even harder to deal with.  Consider, for
> example, the for_each algorithm from STL:
> template <class InputIterator, class Function>
> Function for_each(InputIterator first, InputIterator last, Function f) {
>     while (first != last) f(*first++);
>     return f;
> }
>
> The constraints are as follows:
>   There must be either a built-in operator!=, or a global operator!=,
>     or a member function InputIterator::operator!=, such that applying
>     operator!= to two InputIterators is well-defined and yields something
>     that's convertable to bool.
>   Operators ++ and * are defined such that *InputIterator++ yields an
>     rvalue of some type.
>   An object of type Function has an operator() that takes an argument
>     of the type returned by *InputIterator++, or at least a type to
>     which the return value of *InputIterator++ can be converted.
>     The return value of operator() is unimportant.
>
> Notice what's tricky here: the important constraints are relationships
> between different types, some of which (like the return value of
> InputIterator++, and the return value of *InputIterator++) aren't
> named as template parameters.  This rapidly becomes a problem in
> formal specification.

Yes, sure. I could try to design a class  which messes the whole thing up
even though it provides everything the function needs. This rapidly becomes a
problem in application development :-) Essentially, templates are macros. One
can do a lot of things with macros. But should they be done? Maybe STL the
design of STL would be clearer if everything had to be declared...

>
> This issue is something that has been thought about, and there's some
> discussion of it in D&E.

Yes, there is ( I think so, at least; I don't remember who has my copy but
its not me ). However, in many threads about templates I've read something
like "now the semantics of templates are better understood". I'd like to hear
what people think about them in the light of this new understanding.

Bye

Roman


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "nicolas (n.) chapados" <chapados@bnr.ca>
Date: 1996/04/17
Raw View
Roman Lechtchinsky wrote:

> Now, personally I would prefer to write something like:
>
> template<class T { bool operator==( const T& t ) const; }> ...
>
> Now, what I'd like to know is if something like this has been considered and
> rejected ( and why ) and what other people think about it ( would this be
> really easier to use in practice; do you consider it more elegant than the
> current approach ). Thanks in advance.

Specification of requirements on template arguments in a form similar to
what
you suggest is discussed in Stroustrup's Design and Evolution of C++.
The
reasons leading to the rejection of similar proposals are discussed as
well
(although not to my satisfaction, since one of the stated reasons is
that
requirements specification can be "emulated" in the language--this was
true
when Stroustrup wrote D&E but is not true any more.)

Note that your and similar approach make it easy to specify positive
requirements on arguments ("this type shall have this and that") but not
negative
requirements ("this type shall *not* have this and that").

---
Nicolas Chapados                          ----Standard Disclaimer----
chapados@nortel.ca                        Nortel Technology, formerly
BNR


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "Nathan Myers <http://www.cantrip.org/>" <ncm@cantrip.org>
Date: 1996/04/17
Raw View
Roman Lechtchinsky wrote:

> ... when declaring templates, why isn't it required to declare what
> one expects of the template's parameters?
> ... has [this] been considered and rejected ( and why ) ?

[This is from a paper by Koenig and Stroustrup, "Foundations for
Native C++ Styles" that appeared in Software Practice and Experience
vol. 25, December '95 (a Wiley pub.).  I encourage readers of this
newsgroup to look it up.]

[I hope my excerpting hasn't done too much damage.]

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

What does C++ offer no such facility?   There are three main reasons:

[1] Any such facility would have to take into account not only inheritance
  but also built-in types and operations on types not defined as members.
  ... Ordinary pointers meet the requirements for random-access iterators
  when they are used to point to elements of (built-in) arrays.  That means
  we would need some way of saying that for any type T, T* is a random-
  access iterator, ... [or] forego the ability to use ... [template]
  functions on arrays. ...

[2] The facility would offer little additional safety, if any.  The main
  benefit would be that errors would be detected when a template function
  ... is called, rather than when code is generated for it... [this is]
  not enough to justify a whole new type-checking facility.

[3] Even if such a facility existed ... that would still not guarantee
  safety. ... Specifying "the expected operations" can be messy and
  constraining; specifying "the expected semantics" can be surprisingly
  difficult. ...

In general, we know of no way of expressing constraints on template
parameters that wouldn't be either too cumbersome or too constraining.
...

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

[In short, any such apparatus (in C++, at least) would be complicated,
over-restrictive, and would fail to do the job required of it.  Some
incompatible successor to C++ might be designed with such an apparatus
in mind, but today that's a research topic.]

Nathan Myers
ncm@cantrip.org  http://www.cantrip.org/
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Rob Stewart <stew@datalytics.com>
Date: 1996/04/18
Raw View
Roman Lechtchinsky wrote:
>
> Hi,
>
> sorry for being curious but I'm only human... My question is: when declaring
> templates, why isn't it required to declare what one expects of the
> template's parameters? Let's create a template function:
>
> template<class T> bool equal( const T& t1, const T& t2 )
> {
>   return t1==t2;
> }
>
> Now, personally I would prefer to write something like:
>
> template<class T { bool operator==( const T& t ) const; }> ...
>
> This would make both the development and the use of templates much easier (
> to me, at least ). No need for writing lots of comments explaining what
> interface T should provide; no need for reading the source code of "equal" if
> it is part of a third-party library with poor interface documentation; no
> need to worry about implicit conversions - sounds like a lot of advantages.
> Then again, I imagine that this approach would make templates more efficient
> and easier to implement.
> Now, what I'd like to know is if something like this has been considered and
> rejected ( and why ) and what other people think about it ( would this be
> really easier to use in practice; do you consider it more elegant than the
> current approach ). Thanks in advance.
>

I can't answer whether this has been considered (or rejected),
but I can see a problem with your proposal.  What if a template
required ten, fifteen, twenty mfs in its parameterizing type?
What if those mfs have many parameters?  The resulting list of
mfs and parameters would become quite ungainly.

Perhaps you could declare a class with the exact interface you
expect and provide that as a model for the parameterizing type?
This still leaves issues like equivalence.  Are implicit
conversion allowed in mfs of a parameterizing type in order to
match the model?  At what point do you decide there is no match?

What about the template code?  What if it you misuse the
parameterizing type relative to your model?  Is the model wrong
or is your code wrong?

There's probably a lot more I haven't thought of yet.

--
Robert Stewart  | My opinions are usually my own.
Datalytics, Inc. | stew@datalytics.com


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/04/18
Raw View
Rob Stewart wrote:
>
> Roman Lechtchinsky wrote:
> >
> > Hi,
> >
> > sorry for being curious but I'm only human... My question is: when declaring
> > templates, why isn't it required to declare what one expects of the
> > template's parameters? Let's create a template function:
> >
> > template<class T> bool equal( const T& t1, const T& t2 )
> > {
> >   return t1==t2;
> > }
> >
> > Now, personally I would prefer to write something like:
> >
> > template<class T { bool operator==( const T& t ) const; }> ...
> >[...]
>
> I can't answer whether this has been considered (or rejected),

Yes, it has been. I forgot that D&E contains a ( rather short ) discussion on
this topic. Probably I should have written: "Has there been an attempt to
define this", a question still unanswered.

> but I can see a problem with your proposal.  What if a template
> required ten, fifteen, twenty mfs in its parameterizing type?
> What if those mfs have many parameters?  The resulting list of
> mfs and parameters would become quite ungainly.

Actually, when I define a template I usually describe what it expects from
its parameters in a comment. This means I have to write this list anyway. Do
other programmers do it?


> Perhaps you could declare a class with the exact interface you
> expect and provide that as a model for the parameterizing type?
> This still leaves issues like equivalence.  Are implicit
> conversion allowed in mfs of a parameterizing type in order to
> match the model?  At what point do you decide there is no match?

I'm currently trying to define how this can be handled. In general, I think
the following would do: a function matches a function required by the
template if
a) its return type can be implicitly converted to the required return type
and
b) the types of its parameters can be implicitly converted to the types of
the correspondenting required parameters.
I wonder if someone has already tried this and what the results are.

>
> What about the template code?  What if it you misuse the
> parameterizing type relative to your model?  Is the model wrong
> or is your code wrong?
>

I'm not sure what you mean here.

> There's probably a lot more I haven't thought of yet.
Yes, definitely. However, there's probably a lot more that hasn't been
thought of ( or that is being thought of a this very moment ;-) about
templates as well.

Bye

Roman


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Roman Lechtchinsky <wolfro@cs.tu-berlin.de>
Date: 1996/04/16
Raw View
Hi,

sorry for being curious but I'm only human... My question is: when declaring
templates, why isn't it required to declare what one expects of the
template's parameters? Let's create a template function:

template<class T> bool equal( const T& t1, const T& t2 )
{
  return t1==t2;
}

Now, personally I would prefer to write something like:

template<class T { bool operator==( const T& t ) const; }> ...

This would make both the development and the use of templates much easier (
to me, at least ). No need for writing lots of comments explaining what
interface T should provide; no need for reading the source code of "equal" if
it is part of a third-party library with poor interface documentation; no
need to worry about implicit conversions - sounds like a lot of advantages.
Then again, I imagine that this approach would make templates more efficient
and easier to implement.
Now, what I'd like to know is if something like this has been considered and
rejected ( and why ) and what other people think about it ( would this be
really easier to use in practice; do you consider it more elegant than the
current approach ). Thanks in advance.

Bye

Roman
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: austern@isolde.mti.sgi.com (Matt Austern)
Date: 1996/04/17
Raw View
In article <31741E6C.53CA@cs.tu-berlin.de> Roman Lechtchinsky
<wolfro@cs.tu-berlin.de> writes:

> sorry for being curious but I'm only human... My question is: when declaring
> templates, why isn't it required to declare what one expects of the
> template's parameters? Let's create a template function:
>
> template<class T> bool equal( const T& t1, const T& t2 )
> {
>   return t1==t2;
> }
>
> Now, personally I would prefer to write something like:
>
> template<class T { bool operator==( const T& t ) const; }> ...

Specifying something like this correctly turns out to be extremely
difficult: it's very hard to come up with syntax that neither
overspecifies nor underspecifies the constraints.  In this particular
case, for example: do you really care whether operator== takes a const
T&, as opposed to a T?  Does it matter that it returns bool, instead
of something (like int) that is freely convertable to bool?  Does it
matter whether this is T::operator==(T), or ::operator==(T,T), or any
of a dozen other minor variations?

More realistic cases are even harder to deal with.  Consider, for
example, the for_each algorithm from STL:
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f) {
    while (first != last) f(*first++);
    return f;
}

The constraints are as follows:
  There must be either a built-in operator!=, or a global operator!=,
    or a member function InputIterator::operator!=, such that applying
    operator!= to two InputIterators is well-defined and yields something
    that's convertable to bool.
  Operators ++ and * are defined such that *InputIterator++ yields an
    rvalue of some type.
  An object of type Function has an operator() that takes an argument
    of the type returned by *InputIterator++, or at least a type to
    which the return value of *InputIterator++ can be converted.
    The return value of operator() is unimportant.

Notice what's tricky here: the important constraints are relationships
between different types, some of which (like the return value of
InputIterator++, and the return value of *InputIterator++) aren't
named as template parameters.  This rapidly becomes a problem in
formal specification.

This issue is something that has been thought about, and there's some
discussion of it in D&E.
--
Matt Austern
SGI: MTI Compilers Group
austern@isolde.mti.sgi.com
---
[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]