Topic: Some remarks to N1849 (2nd trial)


Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Sat, 17 Sep 2005 22:22:59 CST
Raw View
Daniel Kr   gler wrote:
> >>// Original concept, as before:
> >>template<typename Iter>
> >>concept RandomAccessIterator : BidirectionalIterator<Iter>
> >>{
> >>typename difference type; // associated type requirement
> >>difference type operator-(Iter const&, Iter const&); // operation
> >>requirement
> >>};
> >>
> >>// Specialized model template, but **differing** from its original:
> >>template<typename T>
> >>concept RandomAccessIterator<const T*>
> >>{
> >>typedef ptrdiff t difference type;
> >>ptrdiff t operator-(const T* x, const T* y) { return x-y; } // Note: No
> >>const& in sight!
> >>};

For reference, our development version of ConceptGCC gives the
following error message when provided with your example:

model_check2.C:17: error: model operation 'ptrdiff_t operator-(const
T*, const T*)' does not match any concept requirement
model_check2.C:9: note: near match: 'typename
RandomAccessIterator<const T*>::difference_type operator-(const T*
const&, const T* const&)'

(The line numbers match up with the operator- in the model and concept,
respectively). Also, much to my surprise, the result is the same
regardless of whether we add an empty where-clause to the model
template, because model templates need to be completely consistent with
their concept even if they have dependent template parameters.

  Doug


---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: =?ISO-8859-1?Q?Daniel_Kr=FCgler?= <dsp@bdal.de>
Date: 15 Sep 2005 06:30:01 GMT
Raw View
{
Introductory comment: This is my 2nd trial to post this contribution.
The previous one was rejected, because the moderator argued, that it was
truncated, but the rejection mail did not show up any truncation and
such the posting is provided below (The only thing I changed where to
replace the single dot in the MyConcept definition by 3 dots).

**This note might be removed by the followup moderator**
}


Hello,

At first I want to emphasize, that I really liked the clearness and
thoroughness of the paper presented on:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1849.pdf

It doesn't evade any details of the proposals problems because it
explicitely mentions them. So although the following points seem
numerous on the first sight, they are mostly harmless (I hope):

1) Class analoga:
- Is it possible to define an object/instance of a concept or model? E.g.

template<typename T>
concept MyConcept {
.
};

MyConcept<int> c;

Assumption: No, it makes no sense, because concepts and models are pure
compile type entities which do have leave runtime traces.

2) I refer again to my most recent posting, where it was answered, that
reuse of typeid has been removed, and that the construction "!typename"
allows disabling of concept checking.

My personally feeling is, that I liked the previous syntax more than new
one. The reason are:

- The change of meaning of typename is more indirect and silent in
combination with concepts, because one has to recognize that there
exists a where clause (positionally quite late) to explain the suddenly
occuring names (injected typenames from concepts). Reusing "typeid"
seems better to me and visually at the right position. It also has the
convenient side effect, that the word "typeid" fits quite well (at least
to me ;-))

- The meaning of "!typename" really seems counterintuitive! My first
impression was: "It is not a type - so what is it?"

Was there somewhat more behind the decision other than: "It seems better
now"? (OK, I see at least one reason, and that is the usage of
simplified template-parameter syntax combined with concept checking
disablement)

3) A second reference to my previous posting: Doug Gregor explained
quite well the difference between loose matching of actual signatures
and the need of exact matching of pseudo signatures. Now the heretical
question: If I write a more specialized concept (i.e. a model), what
says the compiler, if I do the following:

// Original concept, as before:
template<typename Iter>
concept RandomAccessIterator : BidirectionalIterator<Iter>
{
typename difference type; // associated type requirement
difference type operator-(Iter const&, Iter const&); // operation
requirement
};

// Specialized model template, but **differing** from its original:
template<typename T>
concept RandomAccessIterator<const T*>
{
typedef ptrdiff t difference type;
ptrdiff t operator-(const T* x, const T* y) { return x-y; } // Note: No
const& in sight!
};

Is the compiler required to point to the wrong signature of operator-()
in the model for pointers?

4) It is probably a simple question, but one which needs an answer:
which relative positions of a "where" clause and an "inline" keyword are
allowed? Consider

template <typename T>
     where {..}
inline void foo(T) {

---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: "Douglas Gregor" <doug.gregor@gmail.com>
Date: Thu, 15 Sep 2005 21:29:15 CST
Raw View
Daniel Kr   gler wrote:
> At first I want to emphasize, that I really liked the clearness and
> thoroughness of the paper presented on:
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1849.pdf
>
> It doesn't evade any details of the proposals problems because it
> explicitely mentions them.

Thanks!

> So although the following points seem
> numerous on the first sight, they are mostly harmless (I hope):
>
> 1) Class analoga:
> - Is it possible to define an object/instance of a concept or model? E.g.
>
> template<typename T>
> concept MyConcept {
> .
> };
>
> MyConcept<int> c;
>
> Assumption: No, it makes no sense, because concepts and models are pure
> compile type entities which do have leave runtime traces.

Your assumption is absolutely correct.

> 2) I refer again to my most recent posting, where it was answered, that
> reuse of typeid has been removed, and that the construction "!typename"
> allows disabling of concept checking.
>
> My personally feeling is, that I liked the previous syntax more than new
> one. The reason are:

Let's see if we can sway your opinion :)

> - The change of meaning of typename is more indirect and silent in
> combination with concepts, because one has to recognize that there
> exists a where clause (positionally quite late) to explain the suddenly
> occuring names (injected typenames from concepts). Reusing "typeid"
> seems better to me and visually at the right position. It also has the
> convenient side effect, that the word "typeid" fits quite well (at least
> to me ;-))

That was our original rationale. However, while implementing and
testing ConceptGCC we found that the typeid/typename distinction
was too confusing. First, it means that there are now three ways to
declare a template type parameter (typename, class, typeid), two of
which are equivalent but very different from the third. Second, by
using a different keyword here it forced us to explain the difference
between non-dependent (or opaque) template parameters and normal,
dependent template parameters early on in the presentation of generic
programming. But in reality, most users will use one or the other
exclusively: they'll be writing completely type-safe templates or
they'll
be writing C++03 templates, but mixing the two will more likely be
reserved for migrating existing code to type-safe templates.

Finally, and most importantly, "typeid" was just too easy to forget.
While coding ConceptGCC, we of course were writing test cases, mini
STLs, etc; on many occasions I added where clauses to an existing
template, ran it through the compiler, and chuckled with glee when it
worked... only to realize later that I'd forgotten to change the
"typename" parameters to "typeid". Actually, it was worse than that:
even when writing new function templates, I kept writing "typename"
and not "typeid". If the authors/implementors of the proposal can't
use it properly, there is a serious usability problem.

With this new rule, that the presence of any constraints implies that
all template parameters are non-dependent *unless* they are marked
 with '!', the aforementioned problem does not occur. Better yet, it
requires more knowledge to write a less-safe template, and less
knowledge doesn't hurt you. Now, you don't really need to
understand what "dependent" means to write a type-safe template.
When we used "typeid" for non-dependent template parameters,
users had to understand "dependent" before writing their template.

> - The meaning of "!typename" really seems counterintuitive! My first
> impression was: "It is not a type - so what is it?"

Just to be picky, the '!' actually precedes the parameter name, e.g.,

  template<typename !Dangerous> ...

We don't read the '!' as "not", but as "unsafe". There are other
languages
out there that use '!' to mean "unsafe", and we picked up the
convention.
Granted, we don't much care about the syntax here, so long as it takes
extra work to turn off type checking. Someone else suggested to us
privately that we just provide a bult-in concept "std::Unsafe" that
could
be used in a where clause like so:

  template<typename Dangerous> where { std::Unsafe<Dangerous> }

> 3) A second reference to my previous posting: Doug Gregor explained
> quite well the difference between loose matching of actual signatures
> and the need of exact matching of pseudo signatures. Now the heretical
> question: If I write a more specialized concept (i.e. a model), what
> says the compiler, if I do the following:
>
> // Original concept, as before:
> template<typename Iter>
> concept RandomAccessIterator : BidirectionalIterator<Iter>
> {
> typename difference type; // associated type requirement
> difference type operator-(Iter const&, Iter const&); // operation
> requirement
> };
>
> // Specialized model template, but **differing** from its original:
> template<typename T>
> concept RandomAccessIterator<const T*>
> {
> typedef ptrdiff t difference type;
> ptrdiff t operator-(const T* x, const T* y) { return x-y; } // Note: No
> const& in sight!
> };
>
> Is the compiler required to point to the wrong signature of operator-()
> in the model for pointers?

Well, to be picky, the model should have an empty where clause
to turn on type checking for the model template:

  template<typename T> where {}
  concept RandomAccessIterator<const T*>
  {
    // ...
  };

With this change, the program is ill-formed and the compiler shall emit
a
diagnostic stating that the operator-() given does not satisfy any
requirement in the model. The "real" operator- signature will have been
implicitly generated.

If we hadn't added the "where{}", the same error would be detected when
the model is instantiated, e.g., RandomAccessIterator<const int*>;

> 4) It is probably a simple question, but one which needs an answer:
> which relative positions of a "where" clause and an "inline" keyword are
> allowed? Consider
>
> template <typename T>
>      where {..}
> inline void foo(T) {

The where-clause follows a template header or precedes a member
declaration. The relevant portions of the grammar (from page 21 of
N1849) are:

  template-declaration:
    export[opt] template < template-parameter-list > where-clause[opt]
declaration

  member-declaration:
    where-clause member-declaration

  Doug


---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dsp@bdal.de (=?ISO-8859-1?Q?Daniel_Kr=FCgler?=)
Date: Sat, 17 Sep 2005 02:01:31 GMT
Raw View
Hello Douglas Gregor,

At first I want to add that my posting was strongly truncated
through the moderation process, so I tried a second posting in
the main queue and not as answer to your contribution.

Douglas Gregor wrote:
> Daniel Kr=FCgler wrote:
> Let's see if we can sway your opinion :)
>=20

[..]

>=20
> That was our original rationale. However, while implementing and
> testing ConceptGCC we found that the typeid/typename distinction
> was too confusing. First, it means that there are now three ways to
> declare a template type parameter (typename, class, typeid), two of
> which are equivalent but very different from the third. Second, by
> using a different keyword here it forced us to explain the difference
> between non-dependent (or opaque) template parameters and normal,
> dependent template parameters early on in the presentation of generic
> programming. But in reality, most users will use one or the other
> exclusively: they'll be writing completely type-safe templates or
> they'll
> be writing C++03 templates, but mixing the two will more likely be
> reserved for migrating existing code to type-safe templates.
>=20
> Finally, and most importantly, "typeid" was just too easy to forget.
> While coding ConceptGCC, we of course were writing test cases, mini
> STLs, etc; on many occasions I added where clauses to an existing
> template, ran it through the compiler, and chuckled with glee when it
> worked... only to realize later that I'd forgotten to change the
> "typename" parameters to "typeid". Actually, it was worse than that:
> even when writing new function templates, I kept writing "typename"
> and not "typeid". If the authors/implementors of the proposal can't
> use it properly, there is a serious usability problem.
>=20
> With this new rule, that the presence of any constraints implies that
> all template parameters are non-dependent *unless* they are marked
>  with '!', the aforementioned problem does not occur. Better yet, it
> requires more knowledge to write a less-safe template, and less
> knowledge doesn't hurt you. Now, you don't really need to
> understand what "dependent" means to write a type-safe template.
> When we used "typeid" for non-dependent template parameters,
> users had to understand "dependent" before writing their template.
>=20
>=20
>>- The meaning of "!typename" really seems counterintuitive! My first
>>impression was: "It is not a type - so what is it?"
>=20
>=20
> Just to be picky, the '!' actually precedes the parameter name, e.g.,

Yes, you got me here ;-!!


>   template<typename !Dangerous> ...
>=20
> We don't read the '!' as "not", but as "unsafe". There are other
> languages
> out there that use '!' to mean "unsafe", and we picked up the
> convention.
> Granted, we don't much care about the syntax here, so long as it takes
> extra work to turn off type checking. Someone else suggested to us
> privately that we just provide a bult-in concept "std::Unsafe" that
> could
> be used in a where clause like so:
>=20
>   template<typename Dangerous> where { std::Unsafe<Dangerous> }


Yes, that is also I nice idea. But after reading your reasoning of the
proposed nameing and a long time reading bothe papers N1848 and N1849
I think I got used to the propsed syntax.



>>3) A second reference to my previous posting: Doug Gregor explained
>>quite well the difference between loose matching of actual signatures
>>and the need of exact matching of pseudo signatures. Now the heretical
>>question: If I write a more specialized concept (i.e. a model), what
>>says the compiler, if I do the following:
>>
>>// Original concept, as before:
>>template<typename Iter>
>>concept RandomAccessIterator : BidirectionalIterator<Iter>
>>{
>>typename difference type; // associated type requirement
>>difference type operator-(Iter const&, Iter const&); // operation
>>requirement
>>};
>>
>>// Specialized model template, but **differing** from its original:
>>template<typename T>
>>concept RandomAccessIterator<const T*>
>>{
>>typedef ptrdiff t difference type;
>>ptrdiff t operator-(const T* x, const T* y) { return x-y; } // Note: No
>>const& in sight!
>>};
>>
>>Is the compiler required to point to the wrong signature of operator-()
>>in the model for pointers?
>=20
>=20
> Well, to be picky, the model should have an empty where clause
> to turn on type checking for the model template:
>=20
>   template<typename T> where {}
>   concept RandomAccessIterator<const T*>
>   {
>     // ...
>   };
>=20

while you are right concerning the proper way to write this model=20
template it was importand for me to understand what happens, if=20
programmers do make errors in writing models corresponding to concepts.


> With this change, the program is ill-formed and the compiler shall emit
> a
> diagnostic stating that the operator-() given does not satisfy any
> requirement in the model. The "real" operator- signature will have been
> implicitly generated.
>=20
> If we hadn't added the "where{}", the same error would be detected when
> the model is instantiated, e.g., RandomAccessIterator<const int*>;

OK, that are good news!


>>4) It is probably a simple question, but one which needs an answer:
>>which relative positions of a "where" clause and an "inline" keyword ar=
e
>>allowed? Consider
>>
>>template <typename T>
>>     where {..}
>>inline void foo(T) {
>=20
>=20
> The where-clause follows a template header or precedes a member
> declaration. The relevant portions of the grammar (from page 21 of
> N1849) are:
>=20
>   template-declaration:
>     export[opt] template < template-parameter-list > where-clause[opt]
> declaration
>=20
>   member-declaration:=20
>     where-clause member-declaration=20

Thank you for clarification. I have must have found this grammar later=20
than I wrote my question and forgot to remove it...

Greetings,

Daniel

---
[ 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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]