Topic: Missig Nothrow specs. in the standard


Author: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/02/26
Raw View
in article F%Gs4.5103$7f7.10864@nntpserver.swip.net, PETER NORDLUND at
peter_nordlund@swipnet.se wrote on 2/24/00 4:22 AM:

> - Unless thrown by a predicate or a comparison function, the list member
> functions
> remove(), remove_if(), unique(), sort(), and merge() do not throw
> exceptions.
> -----------
> Since I do not have access to the Standard right now, I do not know
> how this "Unless ..." is expressed in the standard document.

It's actually very similar phraseology to what Bjarne writes in his
appendix.

-Dave

---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "PETER NORDLUND" <peter_nordlund@swipnet.se>
Date: 2000/02/24
Raw View
I can give an example what I meant in the original posting with
conditionally Nothrow, although I do not make a definition.


See, for example, the last sentence below:
Citing Bjarnes appendix E.
http://www.research.att.com/~bs/3rd_safe0.html
E.4.1 p 955

[2] Guarantees for list (   17.2.2):
- If an exception is thrown by a push _back() or a push_front(), that
function has no
effect.
- If an exception is thrown by an insert(), that function has no effect.
- No erase(), pop _back(), pop _front(), splice(), or reverse() throws an
exception.
- Unless thrown by a predicate or a comparison function, the list member
functions
remove(), remove_if(), unique(), sort(), and merge() do not throw
exceptions.
-----------
Since I do not have access to the Standard right now, I do not know
how this "Unless ..." is expressed in the standard document.

Regards,
Peter

Scott Meyers <smeyers@aristeia.com> skrev i
diskussionsgruppsmeddelandet:MPG.130ca0d4603afea99896d2@news.supernews.com..
..
> On Sat, 29 Jan 2000 02:46:38 CST, Dave Abrahams wrote:
> > For the benefit of others reading this post, I will clarify that you
mean
> > "conditionally nothrow."
>
> Could you please clarify what you mean by "conditionally nothrow"?  I
> searched the standard.  The phrase doesn't seem to exist.
>
> Thanks,
>
> Scott
>
> --
> "Effective STL" Seminar     June 7-9     Portland, Oregon
> Details at http://www.trekservices.com/estl/
>
> ---
> [ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]
>



======================================= MODERATOR'S COMMENT:

Please so not over-quote. Include only the parts of the original
message that are necessary to understand your reply. In particular,
the .sig and moderation trailer should never be quoted.

---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: smeyers@aristeia.com (Scott Meyers)
Date: 2000/02/15
Raw View
On Sat, 29 Jan 2000 02:46:38 CST, Dave Abrahams wrote:
> For the benefit of others reading this post, I will clarify that you mean
> "conditionally nothrow."

Could you please clarify what you mean by "conditionally nothrow"?  I
searched the standard.  The phrase doesn't seem to exist.

Thanks,

Scott

--
"Effective STL" Seminar     June 7-9     Portland, Oregon
Details at http://www.trekservices.com/estl/

---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Dave Abrahams" <abrahams@mediaone.net>
Date: 2000/01/29
Raw View
In article <thXj4.29$jg4.143@nntpserver.swip.net> , "PETER NORDLUND"
<peter_nordlund@swipnet.se> wrote:

> After reading Bjarnes appendix E.
> http://www.research.att.com/~bs/3rd_safe0.html
> and some other material, I get the impression that the
> STL-algorithms only satisfies the Basic Exception Guarantee.
> (I hope im wrong!)

Aside from the uninitialized_xxx functions, that is correct.

> Moreover there is no Nothrow insert operation.

In general that's impossible, since a container must be prepared to allocate
memory to hold new elements, and elements themselves may throw exceptions
when copied into the container.

> What I like to have is some more Nothrow specifications in
> the standard. Most of them should be conditionally Nothrow.
>
<snip>

> --------
> Background:
> My main problem is that I want to write a function A
> satisfying the Strong Guarantee.
>
> A calls another function B that also satisfies the Strong
> Guarantee, but B has side-effects that cannot be undone if B
> succeeds, e.g. B launches a rocket, erases files
> or something like that.
>
> After calling B (my function A has not finished yet) I want to
> take some action, depending of the outcome of the
> call to B. This means that I can't reorder my operations to do
> e.g. insert of the return value from B into a sorted vector
> before I have called B, I MUST do it afterwards.
> Now I'm in trouble since I have no Nothrow insert.....

There are a number of ways around this. For example, you could make a
container of pointers, and insert a NULL pointer early in function A, then
assign a new pointer value into that container element after B succeeds.
Since the built-in types and standard container iterators don't throw
exceptions, this operation is guaranteed to be nothrow.

> From now on all my calls in the rest of
> function A must be Nothrow. My problem is that there is quite a lot of
> operations in STL that aren't Nothrow, insert is not the only example.
> E.g. the algorithms.
> -------------------
> Nothrow insert:
> I followed Bjarne's example with the vector class in appendix E.
> vector.erase() is Nothrow.
> If erase is Nothrow, I can't see why
> insert also wouldn't be Nothrow, given that V.capacity() > V.size().
>
> (If insert for some reason can't be made Nothrow maybe push_back
> at least could be, (see p 948 in appendix E))
> I would like to be able to do something like:
> vector<int> V;
> ....
> vector<int> tmp;
> tmp.reserve(V.size()+1); // Now I should know that insert won't throw.
> /* Perform call to operation with side-effects. */
> int result = B(....); // On success nothing may fail from now on!
> tmp.push_back(result); // This op should now be Nothrow.
> V.swap(tmp); // Nothrow

Try this instead:

vector<int> V;
.....
vector<int> tmp;
tmp.push_back(0);
vector<int>::iterator last_element = tmp.end() - 1;
/* Perform call to operation with side-effects. */
int result = B(....); // On success nothing may fail from now on!
*last_element = result; // This op should now be Nothrow.
V.swap(tmp); // Nothrow

An even better version would be:

vector<int> V;
.....
V.push_back(0);
try {
    vector<int>::iterator last_element = V.end() - 1;
    /* Perform call to operation with side-effects. */
    *last_element = B(....); // On success nothing may fail from now on!
} catch(...) {
    V.pop_back();
    throw;
}

> -----------------
> Nothrow algorithms:
> I think that a lot of them should be Nothrow.

For the benefit of others reading this post, I will clarify that you mean
"conditionally nothrow."

> As I understood it from reading Bjarnes appe E, algorithms only
> promise to fulfill the Basic Guarantee, but I'm not totally
> shure of what Bjarne really is sayig there.

You have read Bjarne's meaning correctly.

> Take find_if in SGI's impl. as an example:
>
> template <class _InputIter, class _Predicate>
> inline _InputIter find_if(_InputIter __first, _InputIter __last,
>                           _Predicate __pred,
>                           input_iterator_tag)
> {
>   while (__first != __last && !__pred(*__first))
>     ++__first;
>   return __first;
> }
>
> Why is this function not Nothrow according to the standard?

Because the predicate is allowed to throw, and because in general iterator
operations are allowed to throw [Bjarne's appendix makes some statements
about iterators and exceptions which I think are confusing - the actual
situation is that the standard container iterators are not allowed to throw
but all other iterators may throw exceptions].

> (I hope it is, and I am wrong.)

Sorry ;)

> -----------------
> Nothrow insert into a list, a proposal how to do it:
>
> Here I have added quite a lot to SGI's list implementation,
> so I guess this part can't be considered for the current
> standard ;-)

I think nothing of this scope can be considered for the current standard,
although I would be happy to see stronger conditional guarantees in the
standard some day.

> I added a class Node_holder<list<T> > which holds a list node.
> Node_holder basicly has the same
> semantics as auto_ptr, but instead of calling delete in the
> destructor it calls destroy and deallocate.
> Now I can start to pre-allocate the number of Node_holders I need
> first of all. Next when I know that the pre-allocation
> succeeded, I call the function with side effects.
> Now I can safely assign the element in my Node_holder and then
> insert the Node_holder in my list (given that I don't pass L.max_size() )

It sounds like you have one of three situations. Either:
1. The Node_holder initially holds a list node with uninitialized memory for
the list element, which you need to initialize (using placement new) before
you insert the node into your list. In this case you have a serious safety
flaw because an uninitialized element can just as easily be inserted into
the list if you forget to initialize the element.

2. The Node_holder always contains a node with a properly-constructed
element (e.g. the element is default-constructed), and you are using
assignment to initialize it. This case is pretty much equivalent to
constructing a one-element list, and later using splice(), which is
no-throw.

3. You are inducing some sort of undefined behavior, for example using
assignment to get the list element into a node which contains uninitialized
memory.

Case 2 is the only safe alternative, but you could have saved a lot of work
by just using std::list and its splice() operation.

> ----------------
> An undo specification:
> It would be useful if operations have a specification if they are undoable
> somehow or not.
> E.g. list::insert can be undone by list::erase.
> vector::insert may cause a resize of the vector, which might not be undone
> by a vector::erase.

This information is already in the standard. The standard, perhaps
unfortunately, is not always organized or worded conveniently, but in
general we do not introduce redundant information or unneccessary concepts
(like "undo", which would then have to be defined somehow).

I understand the desire to try to milk the standard library components for
the strongest possible guarantees they can offer, believe me. There are,
however, a few other considerations which I believe are worth considering:

1. The more precisely you document the conditional guarantees, the more
complicated the standard becomes. Each algorithm would need to have
accompanying text describing the conditions under which it gave the strong
or nothrow guarantee. People have a hard enough time understanding the
(still relatively simple) patchwork of guarantees that the standard offers
based on the unique characteristics of the various containers. This would
only exacerbate the situation. Also, the chance of mis-specifying something
grows with the amount of specification. We don't want to add any guarantees
that aren't satisfiable.

2. The other problem is that adding more requirements to algorithms and
containers might unduly constrain their implementations. For example, you
might think that there's basically only one way to satisfy the requirements
of std::sort(), but then somebody like Dave Musser comes along and invents
introsort(), which is a substantial improvement over the "obvious" quicksort
implementation. We don't want to prevent people from innovating within the
requirements of the standard.

---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "PETER NORDLUND" <peter_nordlund@swipnet.se>
Date: 2000/01/30
Raw View
Dave Abrahams <abrahams@mediaone.net> skrev i
diskussionsgruppsmeddelandet:h49k4.13757$la6.538262@ndnws01.ne.mediaone.net.
...
> In article <thXj4.29$jg4.143@nntpserver.swip.net> , "PETER NORDLUND"
> <peter_nordlund@swipnet.se> wrote:
>

For clarity I start to repeat assumptions from the original post:
..
>From now on, in the rest of this text,
assume that element-types have all kind of constructors,
assignment-operators etc. Nothrow. All functors are also Nothrow.
And we use the default-allocator.

> > --------
> > Background:
> > My main problem is that I want to write a function A
> > satisfying the Strong Guarantee.
> >
> > A calls another function B that also satisfies the Strong
> > Guarantee, but B has side-effects that cannot be undone if B
> > succeeds, e.g. B launches a rocket, erases files
> > or something like that.
> >
> > After calling B (my function A has not finished yet) I want to
> > take some action, depending of the outcome of the
> > call to B. This means that I can't reorder my operations to do
> > e.g. insert of the return value from B into a sorted vector
> > before I have called B, I MUST do it afterwards.
> > Now I'm in trouble since I have no Nothrow insert.....
>
> There are a number of ways around this. For example, you could make a
> container of pointers, and insert a NULL pointer early in function A, then
> assign a new pointer value into that container element after B succeeds.

Ok, this will work as long as I don't have recursive calls from B to A,
which I may have.
That is: I MUST do the insertion after the call to B.
Otherwise the elements will be put into the container in the wrong order.
But your suggestion to use list::splice(...) will do the trick, I was not
aware of that
function.

> Since the built-in types and standard container iterators don't throw
> exceptions, this operation is guaranteed to be nothrow.
>
> > From now on all my calls in the rest of
> > function A must be Nothrow. My problem is that there is quite a lot of
> > operations in STL that aren't Nothrow, insert is not the only example.
> > E.g. the algorithms.
> > -------------------
> > Nothrow insert:
> > I followed Bjarne's example with the vector class in appendix E.
> > vector.erase() is Nothrow.
> > If erase is Nothrow, I can't see why
> > insert also wouldn't be Nothrow, given that V.capacity() > V.size().
> >
> > (If insert for some reason can't be made Nothrow maybe push_back
> > at least could be, (see p 948 in appendix E))
> > I would like to be able to do something like:
> > vector<int> V;
> > ....
> > vector<int> tmp;
> > tmp.reserve(V.size()+1); // Now I should know that insert won't throw.
> > /* Perform call to operation with side-effects. */
> > int result = B(....); // On success nothing may fail from now on!
> > tmp.push_back(result); // This op should now be Nothrow.
> > V.swap(tmp); // Nothrow
>
> Try this instead:
>
> vector<int> V;
> .....
> vector<int> tmp;
> tmp.push_back(0);
> vector<int>::iterator last_element = tmp.end() - 1;
> /* Perform call to operation with side-effects. */
> int result = B(....); // On success nothing may fail from now on!
> *last_element = result; // This op should now be Nothrow.
> V.swap(tmp); // Nothrow
>
> An even better version would be:
>
> vector<int> V;
> .....
> V.push_back(0);
> try {
>     vector<int>::iterator last_element = V.end() - 1;
>     /* Perform call to operation with side-effects. */
>     *last_element = B(....); // On success nothing may fail from now on!
> } catch(...) {
>     V.pop_back();
>     throw;
> }
>

As I say above: This will not give the same result, if I have recursive
calls from B to A.
I still want vector::insert to be conditionally Nothrow! ;-)
since I need to insert after the call to B.
(For this time I have solved my own problem with the list and splice, but in
general it would
be good to be able to rely on vector::insert being conditionally Nothrow, if
that is possible.)

> > -----------------
> > Nothrow algorithms:
> > I think that a lot of them should be Nothrow.
>
> For the benefit of others reading this post, I will clarify that you mean
> "conditionally nothrow."
>
> > As I understood it from reading Bjarnes appe E, algorithms only
> > promise to fulfill the Basic Guarantee, but I'm not totally
> > shure of what Bjarne really is sayig there.
>
> You have read Bjarne's meaning correctly.
>
> > Take find_if in SGI's impl. as an example:
> >
> > template <class _InputIter, class _Predicate>
> > inline _InputIter find_if(_InputIter __first, _InputIter __last,
> >                           _Predicate __pred,
> >                           input_iterator_tag)
> > {
> >   while (__first != __last && !__pred(*__first))
> >     ++__first;
> >   return __first;
> > }
> >
> > Why is this function not Nothrow according to the standard?
>
> Because the predicate is allowed to throw, and because in general iterator
> operations are allowed to throw [Bjarne's appendix makes some statements
> about iterators and exceptions which I think are confusing - the actual
> situation is that the standard container iterators are not allowed to
throw
> but all other iterators may throw exceptions].
>
> > (I hope it is, and I am wrong.)
>
> Sorry ;)

Ok, an asumption I made early on in the original posting was that all
iterators and
functors should be Nothrow. In this case, the iterators and predicate
(see the conditions in the top of this reply).

I think that the situation is a bit unfortunate, since if I use find_if from
STL I can't rely
on it being conditionally Nothrow, since some vendor could (although quite
unlikely)
come up with some other fancy find_if that is implemented so that it may
throw.

This means that I have to write out the same loop myself, to be sure that I
write portable
Nothrow code.

> > -----------------
> > Nothrow insert into a list, a proposal how to do it:
> >
> > Here I have added quite a lot to SGI's list implementation,
> > so I guess this part can't be considered for the current
> > standard ;-)
>
> I think nothing of this scope can be considered for the current standard,
> although I would be happy to see stronger conditional guarantees in the
> standard some day.
>
> > I added a class Node_holder<list<T> > which holds a list node.
> > Node_holder basicly has the same
> > semantics as auto_ptr,

<snip>

>> Case 2 is the only safe alternative, but you could have saved a lot of
work
> by just using std::list and its splice() operation.

Yes you are right, thanks a lot for pointing that out.
Forget about my suggestion of a Node_holder , it was unneccessary......
I will use list::splice instead.

> > ----------------
> > An undo specification:
> > It would be useful if operations have a specification if they are
undoable
> > somehow or not.
> > E.g. list::insert can be undone by list::erase.
> > vector::insert may cause a resize of the vector, which might not be
undone
> > by a vector::erase.
>
> This information is already in the standard. The standard, perhaps
> unfortunately, is not always organized or worded conveniently, but in
> general we do not introduce redundant information or unneccessary concepts
> (like "undo", which would then have to be defined somehow).
>
> I understand the desire to try to milk the standard library components for
> the strongest possible guarantees they can offer, believe me. There are,
> however, a few other considerations which I believe are worth considering:
>
> 1. The more precisely you document the conditional guarantees, the more
> complicated the standard becomes. Each algorithm would need to have
> accompanying text describing the conditions under which it gave the strong
> or nothrow guarantee. People have a hard enough time understanding the
> (still relatively simple) patchwork of guarantees that the standard offers
> based on the unique characteristics of the various containers. This would
> only exacerbate the situation. Also, the chance of mis-specifying
something
> grows with the amount of specification. We don't want to add any
guarantees
> that aren't satisfiable.
>
> 2. The other problem is that adding more requirements to algorithms and
> containers might unduly constrain their implementations. For example, you
> might think that there's basically only one way to satisfy the
requirements
> of std::sort(), but then somebody like Dave Musser comes along and invents
> introsort(), which is a substantial improvement over the "obvious"
quicksort
> implementation. We don't want to prevent people from innovating within the
> requirements of the standard.

Yes, I understand that. But I think it is quite a difference in complexity
between
sort and find_if. Maybe it is possible to set up conditionally Nothrow
guarantees
on a few simple algorithms without constraining the implementations too
much?
There are a number of situation where it would be quite convenient to have
Notrow
algorithms, (although it would complicate the standard).



---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "PETER NORDLUND" <peter_nordlund@swipnet.se>
Date: 2000/01/28
Raw View
Dear all,

I have a problem. I miss some Nothrow guarantees in the Standard.
I hope that some of the reasonig below could be regarded as
defiecencies in the current standard, if not, maybe it could be
used in antoher upcoming standard....
Note that I do not claim to have understood all implications
of what I'm writing below, but I hope so!

After reading Bjarnes appendix E.
http://www.research.att.com/~bs/3rd_safe0.html
and some other material, I get the impression that the
STL-algorithms only satisfies the Basic Exception Guarantee.
(I hope im wrong!)
I usually have severe difficulties understadig the stadard....

Moreover there is no Nothrow insert operation.
----------------
What I like to have is some more Nothrow specifications in
the standard. Most of them should be conditionally Nothrow.

The extra conditions for Nothrow operations mentioned in this text:
>From now on, in the rest of this text,
assume that element-types have all kind of constructors,
assignment-operators etc. Nothrow. All functors are also Nothrow.
And we use the default-allocator.
--------
Background:
My main problem is that I want to write a function A
satisfying the Strong Guarantee.

A calls another function B that also satisfies the Strong
Guarantee, but B has side-effects that cannot be undone if B
succeeds, e.g. B launches a rocket, erases files
or something like that.

After calling B (my function A has not finished yet) I want to
take some action, depending of the outcome of the
call to B. This means that I can't reorder my operations to do
e.g. insert of the return value from B into a sorted vector
before I have called B, I MUST do it afterwards.
Now I'm in trouble since I have no Nothrow insert.....

(In Bjarne's app E, he says that by carefully ordering your operations
things will work out. But I do not agree with that. As long as you
only perform operations without side-effects, then it might be true,
but when you start doing operations with side effects, you would like
to have quite a lot of Nothrow operations.)

>From now on all my calls in the rest of
function A must be Nothrow. My problem is that there is quite a lot of
operations in STL that aren't Nothrow, insert is not the only example.
E.g. the algorithms.
Wouldn't it be possible to add Nothrow versions of at least a few
algorithms? (More about algorithms below.)
-------------------
Nothrow insert:
I followed Bjarne's example with the vector class in appendix E.
vector.erase() is Nothrow.
If erase is Nothrow, I can't see why
insert also wouldn't be Nothrow, given that V.capacity() > V.size().

(If insert for some reason can't be made Nothrow maybe push_back
at least could be, (see p 948 in appendix E))
I would like to be able to do something like:
vector<int> V;
....
vector<int> tmp;
tmp.reserve(V.size()+1); // Now I should know that insert won't throw.
/* Perform call to operation with side-effects. */
int result = B(....); // On success nothing may fail from now on!
tmp.push_back(result); // This op should now be Nothrow.
V.swap(tmp); // Nothrow
-----------------
Nothrow algorithms:
I think that a lot of them should be Nothrow.
As I understood it from reading Bjarnes appe E, algorithms only
promise to fulfill the Basic Guarantee, but I'm not totally
shure of what Bjarne really is sayig there.

Take find_if in SGI's impl. as an example:

template <class _InputIter, class _Predicate>
inline _InputIter find_if(_InputIter __first, _InputIter __last,
                          _Predicate __pred,
                          input_iterator_tag)
{
  while (__first != __last && !__pred(*__first))
    ++__first;
  return __first;
}

Why is this function not Nothrow according to the standard?
(I hope it is, and I am wrong.)
-----------------
Nothrow insert into a list, a proposal how to do it:

Here I have added quite a lot to SGI's list implementation,
so I guess this part can't be considered for the current
standard ;-)

I added a class Node_holder<list<T> > which holds a list node.
Node_holder basicly has the same
semantics as auto_ptr, but instead of calling delete in the
destructor it calls destroy and deallocate.
Now I can start to pre-allocate the number of Node_holders I need
first of all. Next when I know that the pre-allocation
succeeded, I call the function with side effects.
Now I can safely assign the element in my Node_holder and then
insert the Node_holder in my list (given that I don't pass L.max_size() )

I added a function to the list class called
Node_holder get_node(value_type& v)
This is function returning by value, which makes it a "source".
I also added a function.
iterator insert(iterator it, Node_holder nh); which is a
"sink". After the call to insert the Node_holder is not valid any longer,
since the list takes over the ownership of the
list-node previously owned by the Node_holder.
----------------
An undo specification:
It would be useful if operations have a specification if they are undoable
somehow or not.
E.g. list::insert can be undone by list::erase.
vector::insert may cause a resize of the vector, which might not be undone
by a vector::erase.
----------------

Any comments?

// Peter,
Peter.Nordlund@consultants.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    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]