Topic: COW strings banned because of exceptions? was: Vindicated?
Author: kanze@gabi-soft.fr
Date: Mon, 20 Sep 2004 18:32:33 +0000 (UTC) Raw View
Andrew Koenig <ark@acm.org> wrote in message
news:<8Y62d.595706$Gx4.353507@bgtnsc04-news.ops.worldnet.att.net>...
> <kanze@gabi-soft.fr> wrote in message
> news:d6652001.0409150024.2e0b5b80@posting.google.com...
> > As far as I can see, something like:
> > template< typename CharT >
> > std::basic_string< CharT >::const_reference
> > std::basic_string< CharT >::at() const
> > {
> > throw 3.14159 ;
> > }
> > is a perfectly conforming implementation. Not a very useful one, of
> > course, but the standard doesn't require usefulness.
> I agree.
> IIRC, we had an extended discussion on this issue at the Boston
> meeting, at which we concluded;
> 1) Implementations should be permitted to throw exceptions if they
> exceed their limitations;
> 2) We did not know how to require that implementations should
> exceed their limitations only at times that are convenient to their
> users; and
> 3) Implementations that throw exceptions too often at inconvenient
> times were unlikely to attract many users.
> In other words, we left greater latitude to "quality of
> implementation" issues than some of us might have liked, but only
> because we could not find an unambiguous way of specifying otherwise.
I suspected something along these lines. (And I agree that an
implementation like the above is unlikely to attract many users.) But
isn't there already something in the standard to the effect that
exceeding (unspecified) implementation resource limits is undefined
behavior? So even without the above, the implementation could throw an
exception. Or crash, or reformat the hard disk, or...
Or is the presence of this sentence to be interpreted as a sort of a
very weak recommendation that throwing an exception might be a better
solution than some of the alternatives? (IMHO, it's certainly a better
solution than formatting the hard disk. Compared to simply crashing, or
invoking a user defined callback which aborts by default, I'm less
sure.)
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
[ 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: kanze@gabi-soft.fr
Date: Mon, 20 Sep 2004 19:33:42 +0000 (UTC) Raw View
Herb Sutter <hsutter@gotw.ca> wrote in message
news:<2dgjk0hpr9iium9i28g69naveigescmq4r@4ax.com>...
> >If you think that it is a defect, or at least, something we should
> >change, I'm behind you 100%. It means that in theory, at least, it
> >is impossible to write exception safe code without depending on
> >implementation defined behavior.
> That's my fear now. Let me use this article to repost in this new
> thread the essentials of a separate reply on the original thread.
Good idea. That consolidates the discussion in one thread.
> To recap, the basic point under discussion is what "Throws:"
> specifications actually specify: Are they restrictive, or can
> implementations throw additional types of exceptions and/or under
> different circumstances than described in a "Throws:" clause?
> Some interpret 17.4.4.8(1) and (3) to mean that only exception
> specifications may not be violated, and that "Throws:" clauses are not
> restrictive so that implementations are free to throw additional
> exceptions and/or under additional circumstances.
> Others interpret 17.4.4.8(1) to mean that Throws: clauses and
> exception specifications taken together may not be violated, thus that
> both are restrictive so that implementations are not free to throw
> additional exceptions and/or under additional circumstances. In
> particular, (1) doesn not say that a standard library function "can"
> report a failure by throwing other types of exceptions. Further, we
> have 17.3.1.3(3) which says that "Throws:" clauses document "any
> exceptions thrown by the function, and the conditions that would cause
> the exception."
Note that Andrew Koenig and Dave Abrahams have weighed in saying that
the first interpretation was what was intended. Given their respective
roles in the standardization and the definition of exceptions in the
library, I find that rather conclusive.
With regards to intent, at least.
> Here are a few potential defects:
> 1. In 17.4.4.8(1), almost certainly "can" should be replaced by
> "shall" at least for "Throws:" paragraphs, unless we really intended
> to additionally grant license for functions to not throw the
> specificed exceptions under the specified conditions in "Throws:"
> paragraphs. Alternatively, we could change "would" to "shall" in
> 17.3.1.3(3) line 6.
I sort of agree, which perhaps one hesitation -- what happens if the
implementation exceeds some resource limit at the same time it
encounters the condition requiring the exception? But more on this
later. (I mention it here because Andy mentionned it as the motivation
for the current wording.)
> 2. We need to reword 17.4.4.8 and 17.3.1.3 to make it clear whether or
> not "Throws:" is restrictive.
> 3. If we intended/decide that "Throws:" shouldn't be restrictive, we
> probably want to fix some functions whose Throws: paragraphs appear to
> be inappropriate under that interpretation, including the "Throws:
> Nothing" paragraphs in Clause 23.2.2 (std::list) which AFAIK were
> definitely intended to be restrictive (e.g., list::splice).
> 4. If we intended/decide that "Throws:" should be restrictive, we
> probably want to fix a different set of functions whose Throws:
> paragraphs appear to be inappropriate under _that_ interpretation,
> including the basic_string constructor that takes another string and
> offset(s), whose Throws: appears in 21.3.1(4) and should probably
> allow more than just out_of_range (e.g., bad_alloc if allocation
> fails).
I'll throw the following idea out for discussion:
- We reword 19.4.4.8 to say that both Throws: and exception
specification are normative: the Throws: specifies what the function
*must* throw, under what condition, and the exception specification
specifies what it can throw.
- We add text (probably in the form of a note) there to remind that in
all cases, exceeding implementation limits is undefined behavior,
which means that, amongst other things, the implementation can throw
anything it wants.
- We then clean up all of the Throws: clauses and all of the exception
specifications to conform to what is wanted, according to the
above. In particular, Throws: nothing should disappear.
On the other hand, maybe there is an advantage in indicating somehow
that an implementation should try to avoid throwing from a
particular function, if possible, without making it absolutely
mandatory. (I'm not sure if I am really clear here. My idea is
something along the lines that if an implementation does detect when
resources are exceeded, and throws, that it would be better if at
all possible that it do so in another function.)
Anyhow, as I said, these are just ideas for discussion. I wasn't
present when the issue was originally discussed, and there are probably
important points which I'm not taking into account.
Also, we shouldn't ignore the point that the current specification IS
working. There is some value in leaving a maximum of freedom to the
implementation, as long as it is understood that that freedom will only
be used when absolutely necessary. And that is, in fact, the case
today. Implementations aren't abusing this freedom.
> James notes:
> >I don't know if this is a defect in the standard, or whether it was
> >intentional. One can easily imagine that the authors really intended
> >to cover three cases:
> > - No specifications as to what happens on implementation defined
> > errors -- the function has neither a throws clause nor an exception
> > specification. (This is the most common cases.)
> > - The standard specifies exactly and fully all possible exceptions:
> > the function has an exception specification (and probably a throws
> > clause as well).
> > - The standard specifies precise exceptions for certain types of
> > errors, but leaves the implementation free, as in the first case,
> > for all other possible errors: the function has a throws clause, but
> > no exception specification.
> >Unless there is a defect report on this, I'd say that the most probable
> >intention was to allow the three cases,
> >If you think that this is a misinterpretation of the standard, please
> >explain why.
> This is one possible intent, but I don't think the above is consistent
> with the "Throws: Nothing" clauses on std::list functions, which would
> fall into your third case but which almost certainly were intended to
> be guaranteed-nonthrowing operations.
Agreed. My comments only concern 17.4.4.8; it is quite possible that
there are inconsistencies elsewhere.
Of course, it is also possible that the intent behind the "Throws:
Nothing" clauses is to simply recommend: don't throw if you can possibly
avoid it (as opposed to "throwing here will make you non-conformant",
e.g. as in destructors or when there is an empty exception
specification).
I do think that there is some justification for three levels of
specification:
- A fully restrictive one: the standard specifies exactly what should
be thrown, and when. Of course, the "exceeding resources" cop-out
still exists. If you get a stack overflow when calling
basic_string::at(), then what happens is undefined behavior, and
there is no guarantee that you will get an out_of_range, even if
your index is out of range.
- A recommended practice: the standard suggests what is the preferred
behavior, but an implementation doesn't automatically cease to
become non conformant if for some reason, this preferred behavior is
not reasonable on its target platform.
- The standard leave full liberty up to the implementation. Of hand,
I'd suggest that this be the case for most non-const functions, with
just enough exceptions to make exception safe code possible
(e.g. functions like swap).
> I suspect that no single interpretation of "Throws:" clauses in the
> current standard can be consistent right now, because "Throws:"
> appears to be used inconsistently -- nonrestrictively in some places
> (e.g., std::basic_string) and restrictively in others (e.g.,
> std::list).
It's hard to say, because the standard never really officially takes a
position on recommended pratices. It does so in an ad hoc manner
(e.g. like in the case of the semantics of a reference_cast). If the
Throws: clauses really are just "recommended practice", maybe what you
see as inconsistency is intentional. (Of course, if this is the case,
I'd much prefer that the standard says so explicitly. Something along
the lines of "The Throws: paragraphs are not normative, but it is
expected that an implementation respect them whenever reasonable.")
--
James Kanze GABI Software http://www.gabi-soft.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
[ 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: Andrew Koenig <ark@acm.org>
Date: Fri, 17 Sep 2004 15:29:55 +0000 (UTC) Raw View
<kanze@gabi-soft.fr> wrote in message
news:d6652001.0409150024.2e0b5b80@posting.google.com...
> As far as I can see, something like:
>
> template< typename CharT >
> std::basic_string< CharT >::const_reference
> std::basic_string< CharT >::at() const
> {
> throw 3.14159 ;
> }
>
> is a perfectly conforming implementation. Not a very useful one, of
> course, but the standard doesn't require usefulness.
I agree.
IIRC, we had an extended discussion on this issue at the Boston meeting, at
which we concluded;
1) Implementations should be permitted to throw exceptions if they
exceed their limitations;
2) We did not know how to require that implementations should exceed
their limitations only at times that are convenient to their users; and
3) Implementations that throw exceptions too often at inconvenient times
were unlikely to attract many users.
In other words, we left greater latitude to "quality of implementation"
issues than some of us might have liked, but only because we could not find
an unambiguous way of specifying otherwise.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
[ 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: Herb Sutter <hsutter@gotw.ca>
Date: Fri, 17 Sep 2004 15:29:56 +0000 (UTC) Raw View
>If you think that it is a defect, or at least, something we should
>change, I'm behind you 100%. It means that in theory, at least, it is
>impossible to write exception safe code without depending on
>implementation defined behavior.
That's my fear now. Let me use this article to repost in this new thread
the essentials of a separate reply on the original thread.
To recap, the basic point under discussion is what "Throws:"
specifications actually specify: Are they restrictive, or can
implementations throw additional types of exceptions and/or under
different circumstances than described in a "Throws:" clause?
Some interpret 17.4.4.8(1) and (3) to mean that only exception
specifications may not be violated, and that "Throws:" clauses are not
restrictive so that implementations are free to throw additional
exceptions and/or under additional circumstances.
Others interpret 17.4.4.8(1) to mean that Throws: clauses and exception
specifications taken together may not be violated, thus that both are
restrictive so that implementations are not free to throw additional
exceptions and/or under additional circumstances. In particular, (1) doesn
not say that a standard library function "can" report a failure by
throwing other types of exceptions. Further, we have 17.3.1.3(3) which
says that "Throws:" clauses document "any exceptions thrown by the
function, and the conditions that would cause the exception."
Here are a few potential defects:
1. In 17.4.4.8(1), almost certainly "can" should be replaced by "shall" at
least for "Throws:" paragraphs, unless we really intended to additionally
grant license for functions to not throw the specificed exceptions under
the specified conditions in "Throws:" paragraphs. Alternatively, we could
change "would" to "shall" in 17.3.1.3(3) line 6.
2. We need to reword 17.4.4.8 and 17.3.1.3 to make it clear whether or not
"Throws:" is restrictive.
3. If we intended/decide that "Throws:" shouldn't be restrictive, we
probably want to fix some functions whose Throws: paragraphs appear to be
inappropriate under that interpretation, including the "Throws: Nothing"
paragraphs in Clause 23.2.2 (std::list) which AFAIK were definitely
intended to be restrictive (e.g., list::splice).
4. If we intended/decide that "Throws:" should be restrictive, we probably
want to fix a different set of functions whose Throws: paragraphs appear
to be inappropriate under _that_ interpretation, including the
basic_string constructor that takes another string and offset(s), whose
Throws: appears in 21.3.1(4) and should probably allow more than just
out_of_range (e.g., bad_alloc if allocation fails).
James notes:
>I don't know if this is a defect in the standard, or whether it was
>intentional. One can easily imagine that the authors really intended to
>cover three cases:
>
> - No specifications as to what happens on implementation defined
> errors -- the function has neither a throws clause nor an exception
> specification. (This is the most common cases.)
>
> - The standard specifies exactly and fully all possible exceptions:
> the function has an exception specification (and probably a throws
> clause as well).
>
> - The standard specifies precise exceptions for certain types of
> errors, but leaves the implementation free, as in the first case,
> for all other possible errors: the function has a throws clause, but
> no exception specification.
>
>Unless there is a defect report on this, I'd say that the most probable
>intention was to allow the three cases,
>
>If you think that this is a misinterpretation of the standard, please
>explain why.
This is one possible intent, but I don't think the above is consistent
with the "Throws: Nothing" clauses on std::list functions, which would
fall into your third case but which almost certainly were intended to be
guaranteed-nonthrowing operations.
I suspect that no single interpretation of "Throws:" clauses in the
current standard can be consistent right now, because "Throws:" appears to
be used inconsistently -- nonrestrictively in some places (e.g.,
std::basic_string) and restrictively in others (e.g., std::list).
Herb
---
Herb Sutter (www.gotw.ca)
Convener, ISO WG21 (C++ standards committee) (www.gotw.ca/iso)
Contributing editor, C/C++ Users Journal (www.gotw.ca/cuj)
Architect, Developer Division, Microsoft (www.gotw.ca/microsoft)
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
[ 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 ]