Boost logo

Boost :

From: Gavin Lambert (boost_at_[hidden])
Date: 2019-06-27 00:11:18


On 26/06/2019 20:26, Andrey Semashev wrote:
>> Most of the time this Just Worksâ„¢;
>
> I'd say, most of the time it doesn't work. Including any foreign
> headers, including standard library headers, in lib.hpp will break the
> program at some stage, either compilation or linking.

True, although there are workarounds for that; the simplest being that
if you already #included everything that the library header does outside
of the namespace (except for those things that you do want inside the
namespace), then include guards will usually prevent the standard
headers being included inside the namespace. (assert.h being a notably
annoying exception, as that traditionally lacks guards for some
inexplicable reason.)

Of course, this also means that you need to defeat the guards of the
library itself -- but this typically happens for free if it uses #pragma
once guards rather than explicit #defines.

And macro definitions in the library can be a problem as well. So
you're right, it's not universally applicable, though it can be very
useful in specific circumstances.

I don't know whether modules will make this scenario better or worse. :)

> I'm not saying I like specialization as a way of customization though.

There's exactly two places where a specialization of C<T> can be
written: immediately below C, or immediately below T. Anywhere else can
give you extreme grief.

Of course, ADL isn't much better in that regard, except that you don't
have to mess with namespaces as much. (Although actually calling ADL
functions correctly can be troublesome in its own way.)

>> But what if the function has two output parameters?  Memory is
>> allocated for both and returned as a raw pointer by the function.  One
>> of the out_ptrs is then destroyed and calls reset(), and this throws.
>> The other out_ptr is then destroyed as well, and now it has a problem;
>> it either has to call reset() as well and possibly throw yet again, or
>> leak memory.
>
> Good point. In that case I see no good solution, given that on error
> there may be garbage in the out pointers.

To clarify, I meant that the function itself succeeds (and so does not
return garbage), but then cleanup of the function arguments is where the
exception is thrown. Especially as this occurs in unspecified order.

A similar problem can occur if construction of one of the function
arguments fails -- if the out_ptr hasn't been constructed yet, then it
won't be destructed either and there's no change in behaviour. If it
has, then it will be destructed and will try to reseat its raw pointer
into a smart pointer -- which will only work correctly for out_ptr if it
always initialises its raw pointer to nullptr explicitly, as mentioned
below. For inout_ptr it's not a problem -- it just release()d a pointer
and then reset() the same one right back again.

If the exception occurs after the function call full-expression, then
the raw pointers have been successfully saved into smart pointers, and
there shouldn't be a problem any more. Similarly if it occurs before
the call expression starts.

If the function itself throws, it is its duty to avoid leaking memory
(basic guarantee) and to not output deleted pointers (either by
explicitly setting them to nullptr or leaving them at the same value as
on input). Conversely, it's out_ptr's responsibility to always provide
a nullptr as input. For inout_ptr, that responsibility falls to the
caller to provide a valid possibly-not-nullptr pointer.


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