Boost logo

Boost :

From: Gennaro Prota (gennaro_prota_at_[hidden])
Date: 2002-11-07 07:26:27


On Wed, 6 Nov 2002 16:21:34 -0300, "Fernando Cacciola"
<fernando_cacciola_at_[hidden]> wrote:

>OK, my fault.
>I wasn't thinking of directly using StrongStorage<T>...
>I was thinking of borrowing its idea, so I meant that using some variation
>of the double-buffer technique, you could achieve no-throw. Though I should
>have said so explicitely, sorry.

Yeah, I understood that (I'm not so 'inelastic'! :-). However your
insistence on no-throw made me go to the original code to show that
not even there there's a no-throw guarantee. Of course, I agree with
you that the technique allows us to have the strong guarantee.

>>
>> Now, as I said, I DO NOT have the code of construct_as () because I
>> haven't found it in any place, but if it uses copy construction (as I
>> suppose) and if the copy may throw then that swap cannot provide the
>> nothrow guarantee. We agree on that, I think. Now let us see other
>> points:
>>
>You are right here, construct_as(), which is in "aligned_storage.hpp" in the
>sandbox, uses the copy ctor, so StrongStorage<T>::swap is not designed as
>no-throw.

Ok.

[...]
>> > restore_previous_state_without_throwing();
>>
>> Same note, of course.
>>
>OK. Since I've actually cheated by showing what should be done instead of
>how to do it, I've noticed that this couldn't possible work AFAICT. This
>restore, unlike the save, not only should not throw but also succeed: i.e:
>copy back all elements without failure.

Exactly. That's why I asked how did you do that. I agree however that
the show-stopper is the restore not the save part.

>
>> > throw;
>> > }
>> >}
>> >
>> >A no-throw swap would be essentially the same except that exception
>possibly thrown by the T's swap are eaten.
>> >
>> >Do we agree that the above is the minimun 'traditional' implementation?
>>
>> It depends. Don't make me say that :-) Once you are in the catch and
>> you have swapped N/2 objects...
>>
>Before we get into the details of the double buffer, let me try to put my
>thoughts into perspective.
>We have an Array class which has two internal arrays: one dynamically
>allocated and another automatically allocated.
>If I am understanding the design correctly, at any time, only one of these
>internal arrays represents the state of the Array.
>If it is the dynamic array, then swap is not a problem since we just swaps
>pointers. That's easy.
>But what if the automatic array is the one currently active?
>How would you implement swap() and which exceptions guarantees would you
>achieve?
>-- please answer here --

Well, I agreed with you since the beginning that the technique is
useful for the strong guarantee. I hope that's clear now. My objection
concerned "no throw".

>Now, without the double buffer, my only answer to the question above *would*
>be the 'traditional' implementation I've shown. However, even that doesn't
>work because the restore cannot be made to succeed, AFAICT, in the presence
>of throwing copy ctor.
>
>So, since *I* do not have a clue about how to implement a swap with at least
>strong guarantees without the double buffer, I propose it.
>However, you or someone else might know how to do the same without this
>complicated scheme (and without relying on the presence of a no-throw T's
>swap). In that case, though, I will love to hear about it.

I don't know either. However I'm still not convinced that a strongly
exception-safe swap is really useful for an array-like class like
that. I'm not saying it isn't, only that I'm not convinced it is. Do
you have any example that can make me change my mind?

>> > if ( storage_initialized_[mirror_idx] )
>> > lhs_mirror->~T();
>>
>> Who says you that you can invoke the destructor on all the N elements?
>
>This code is destructing the element which has been previously stored in the
>'mirror' storage.

I think you missed my point. You don't know how many Ts have been
successfully constructed in the storage. Let's say N=8 and the 3rd
copy constructor fails: you must destroy only the first 2 elements.
But that was a minor note that I had better left aside considering the
extemporaneousness of the code. Furthermore that's trivial to fix by
preserving the value of i and looping backward.

Ok so far. Now that we have understood each other it's time for
another problem :-) Can you think of a situation where a swap between
arrays with different values of N is useful? That adds special cases
to the code, of course: suppose e.g. you want to swap an array<T, 10>
that happens to have 12 elements and an array<T, 15> that has 5
elements. It's easy but you have to stuff the code with several ifs.
And that's one of the aspects I was thinking to when saying that a)
I'm not sure there's a remarkable gain in performance (of course there
can be for some values of N but... maybe on your platform only, maybe
for some scenarios only... a complex story IMHO) and b) if a gain
exists, if it is worth the complexity.

Also note that, in the end, the user can only count on the strong
guarantee for swap. One wants to know in compile-time what are the
guarantees, of course. Our array<T, N>::swap() instead offers
different guarantees depending on whether it has more than N elements
or not. Obviously this means that, generally, the designer of the
client code will rely on the weakest of the two.

Genny.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk