 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
kanze@gabi-soft.fr Guest
|
Posted: Wed Sep 15, 2004 8:13 pm Post subject: COW strings banned because of exceptions? was: Re: Vindicate |
|
|
Herb Sutter <hsutter (AT) gotw (DOT) ca> wrote
| Quote: | On 14 Sep 2004 08:22:11 -0400, David Abrahams
[email]dave (AT) boost-consulting (DOT) com[/email]> wrote:
Herb Sutter <hsutter (AT) gotw (DOT) ca> writes:
Finally, it's worth noting that std::string has even more
constraints for COW than you or I have mentioned here, and that
are entirely unrelated to thread safety. In particular, it's not
clear to me how any COW implementation of std::string can be
standards-conforming because of this situation (which I've been
pointing out for years in hallway conversations and talks, but it
now occurs to me I don't think I've written about):
string s1( "Hello" );
string s2( s1 );
According to the standard, I don't believe a conforming
implementation may do a shallow copy here. The reason is that
there's no way to know whether the very next use of the string
will mention op[]:
s1[0]; // or, s2[0]
If string did use COW and did a shallow copy when constructing s2,
then str[0] must do a deep copy, which means it will allocate
memory, which means it could throw a bad_alloc exception, and that
is nonconforming because when the index is within bounds no
exception may be thrown.
Since when? Standard references please!
I think there may be reasons that a conforming COW string is
impossible, but this ain't one of them, AFAICT.
Ah, I forgot string []/at mirrored vector's. (We must fix that, but
that's a separate issue.) So just change [] to at:
s1.at(0); // or, s2.at(0)
|
Note that neither operator[] nor at() have exception specifiers in my
copy of the standard (the 1998 version).
| Quote: | Now it's clear that the above is not allowed to throw,
|
Even considering the second sentence in §17.4.4.8/3: "Any other
functions [than a destructor] defined in the C++ Standard Library that
do not have an exception-specification may throw implementation-defined
exceptions unless otherwise specified."
| Quote: | but could do so on a COW implementation.
For those keeping score at home, this is because of wording in the
standard that Dave himself was instrumental in crafting:
17.4.4.8(1)
Any of the functions defined in the C + + Standard Library can
report a failure by throwing an exception of the type(s) described
in their Throws: paragraph and/or their exception-specification
(15.4). An implementation may strengthen the exception-
specification for a non-virtual function by removing listed
exceptions. 175)
Footnote 175:
That is, an implementation of the function will have an explicit
exception-specification that lists fewer exceptions than those
specified in this International Standard. It may not, however,
change the types of exceptions listed in the exception-
specification from those specified, nor add others.
In short, the standard library, any operation with a "Throws:"
paragraph may throw only listed exceptions.
|
That's not quite what the text you quote says. Although the first
sentence speaks of "their Throws: paragraph and/or their
exception-specification", the others only say
"exception-specification". And neither operator[] nor at() have an
exception specification.
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, and that at() falls into the
third -- an illegal index throws a well defined exception, but an
implementation is free to throw an implementation defined exception.
(IMHO, footnote 178 seems to confirm this interpretation.)
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.
If you think that this is a misinterpretation of the standard, please
explain why.
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. (Note, for example, that
std::string::swap doesn't have an exception specification either.)
| Quote: | For basic_string<>::at, we have:
21.3.4(3):
Throws: out_of_range if pos >= size().
That is, at may not throw an exception of any type besides
out_of_range, and it may throw no exceptions if pos < size().
|
All that says is that basic_string<>::at MUST throw the designated
exception for this particular error. The paragraph you quote says that
a function "can" report an error by means of the specified exception.
It does not say that it "shall not" raise any other exceptions.
(On rereading it: I don't like the use of "can" either. Normally,
requirements on an implementation use the verb "shall". I would much
prefer text which clearly required an implementation to report errors
decribed in the Throws: clause by means of the described exception, or
one derived from it. I rather think that this was the intent, but my
interpretation of "can", in the context of the standard, is that an
implementation is allowed to, but not required to.)
At any rate, I've changed the subject, and added comp.std.c++ to the
groups, since this really has nothing to do with your implementation of
COW strings. (I'd also have set followups if my newsreader allowed it.)
| Quote: | That being the case here, COW cannot be a conforming implementation of
std::string AFAICS because of the given sequence:
|
--
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 ]
|
|
| Back to top |
|
 |
David Abrahams Guest
|
Posted: Thu Sep 16, 2004 3:14 pm Post subject: Re: COW strings banned because of exceptions? |
|
|
[email]kanze (AT) gabi-soft (DOT) fr[/email] writes:
| Quote: | 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.
If you think that this is a misinterpretation of the standard, please
explain why.
|
You understand correctly.
| Quote: | 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 already possible.
| Quote: | (Note, for example, that std::string::swap doesn't have an exception
specification either.)
|
Bingo. That should be changed, just to keep people from making
mistakes with it. But it's not, strictly speaking, a defect.
| Quote: | For basic_string<>::at, we have:
21.3.4(3):
Throws: out_of_range if pos >= size().
That is, at may not throw an exception of any type besides
out_of_range, and it may throw no exceptions if pos < size().
All that says is that basic_string<>::at MUST throw the designated
exception for this particular error.
|
I'm not even sure it's that strong.
| Quote: | The paragraph you quote says that a function "can" report an error
by means of the specified exception. It does not say that it "shall
not" raise any other exceptions.
(On rereading it: I don't like the use of "can" either. Normally,
requirements on an implementation use the verb "shall". I would much
prefer text which clearly required an implementation to report errors
decribed in the Throws: clause by means of the described exception, or
one derived from it. I rather think that this was the intent, but my
interpretation of "can", in the context of the standard, is that an
implementation is allowed to, but not required to.)
|
Correct. I think it's meant to be "guidance for implementors".
--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com
[ 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 ]
|
|
| Back to top |
|
 |
Andrew Koenig Guest
|
Posted: Fri Sep 17, 2004 3:29 pm Post subject: Re: COW strings banned because of exceptions? was: Re: Vindi |
|
|
<kanze (AT) gabi-soft (DOT) fr> wrote
| Quote: | 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 ]
|
|
| Back to top |
|
 |
Herb Sutter Guest
|
Posted: Fri Sep 17, 2004 3:29 pm Post subject: Re: COW strings banned because of exceptions? was: Re: Vindi |
|
|
| Quote: | 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:
| Quote: | 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 ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Sep 20, 2004 6:32 pm Post subject: Re: COW strings banned because of exceptions? was: Re: Vindi |
|
|
Andrew Koenig <ark (AT) acm (DOT) org> wrote
| Quote: | kanze (AT) gabi-soft (DOT) fr> wrote in message
news:d6652001.0409150024.2e0b5b80 (AT) posting (DOT) 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 ]
|
|
| Back to top |
|
 |
kanze@gabi-soft.fr Guest
|
Posted: Mon Sep 20, 2004 7:33 pm Post subject: Re: COW strings banned because of exceptions? was: Re: Vindi |
|
|
Herb Sutter <hsutter (AT) gotw (DOT) ca> wrote
| Quote: | 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.
| Quote: | 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.
| Quote: | 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.)
| Quote: | 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.
| Quote: | 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).
| Quote: | 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 ]
|
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|