I've compiled the results of the reviews of out_ptr during its recent
review.
Unfortunately, out_ptr is NOT ACCEPTED, though many of the reviewers
indicated interest in seeing it come back in some future form.
I counted these formal reviews:
Andrey Semashev: reject
Louis Dionne: accept
David Sankel: reject
Phil Endecott: reject
Glen Fernandes: reject
Gavin Lambert: reject
Thanks to all of the above reviewers, and to all those who contributed to
the ongoing discussions surrounding out_ptr, even if they did not have time
for a full review.
Points against out_ptr raised during the review:
- Many reviewers indicated their puzzlement that this library was proposed
at all, since they had never seen enough calls to C APIs in their own code
to motivate the need for out_ptr.
- Some reviewers who did find the motivating cases of the library
compelling, still said that they would prefer to write wrappers instead of
using something like out_ptr.
- Multiple reviewers want the library to provide error-reporting help (it
currently does not). In particular, for c_func(out_ptr(p)), there was a
concern about whether p is assigned a value, when c_func() returns a
failure-status.
- There was concern from several reviewers about the fact that out_ptr's
types' dtors may throw, though this is only limited to the use of out_ptr
with shared_ptr and similar smart pointers. In particular, non-unique_ptr
uses will never throw in out_ptr's types' dtors, unless the user provides a
throwing deleter.
- Multiple reviewers disagreed with the use of template specialization as a
customization mechanism.
- There was widespread concern about the use of potentially UB-invoking
tricks to get max performance out of the implementation.
- Many reviewers wanted a more typical Boost tutorial introduction in the
documentation.
Now please permit me a digression.
The library attempts to take a call that looks like this (simplest call
possible):
// CASE 0
foo * new_foo;
TODO
if (!create_foo(&new_foo)) {
// etc. ...
}
And turn it into something as close as possible to this:
// CASE 1
std::unique_ptr<foo> new_foo;
if (!create_foo(new_foo)) {
// etc. ...
}
out_ptr does a great job of this lexically:
// CASE 2
std::unique_ptr<foo> new_foo;
if (!create_foo(out_ptr(new_foo))) {
// etc. ...
}
If you have not written thousands of lines of code that look like case 0,
stop for a second and consider what it would be like to drop those
thousands of raw pointers into your code, and how much you would like to
replace them all with unique_ptrs.
Now consider what it would take you to write all the wrapping functions
necessary to make those thousands of calls look like case 1. You may make
tooling to generate the wrappers, and that's time consuming. Writing it by
hand is even more so.
out_ptr also has the nice property that using it as I have above with
unique_ptr does not change the meaning of the code, except to ensure that
new_foo is guaranteed to be treated in a RAII manner. Thus we get
increased exception safety, memory leak mitigation, etc. -- y'know, all
that RAII stuff. If out_ptr is implemented well, case 2 should produce
nearly identical object code to case 0, except that case 2 handles calling
new_foo's dtor at the right time.
Note that because of this, you have the same relationship to the C API in
all three cases. That is, you need to check for the success value returned
from create_foo(), and you need to know what create_foo()'s contract is in
the face of errors -- did it write to new_foo or not? out_ptr is how you
get from raw pointers to RAII ones, not how you solve all programming
problems related to calling functions.
Hopefully that motivates the use case for out_ptr, even if you don't need
it in your own code. Hopefully that also allays concerns that out_ptr is
not doing enough -- doing more is outside the library's scope.
Now, on to exceptions in out_ptr's types' dtors. I heard a lot of
discussion about this during the review. To me, concerns about what
happens when an out_ptr or an inout_ptr throws in its dtor misses the point
entirely, because it is concerned with fixing corner cases we should not
even care to support. Instead of worrying about the semantics of
exceptions in those kinds of situations, we should instead be asking why we
want to support user code like this:
... } catch {
create_foo(in_out_ptr(my_shared_ptr_to_a_foo));
...
}
In other words, we don't really care about the dtor exceptions, unless:
- there is an active exception already, and
- we are also calling a C API to create a new object (a possible, but
certainly atypical thing to do when handling an exception), and
- we're catching this new object in a shared_ptr-like smart pointer that
might throw on construction.
Who writes code like that? Few people, perhaps none. It's not impossible
to see code like that, but until there are well-motivated cases for this,
maybe leave it for a later version of the library.
In other words, the question should have been "Why support anything other
than nothrow-constructible RAII types like unque_ptr?" Note that I find
this argument compelling for the WG21-facing version of this work too.
To summarize: I think the first four bullet points listed at the beginning
of this post are, or could easily be made to be, irrelevant.
So, that leaves some issues still outstanding:
- figure out how to provide customization points (I'm actually fine with
the current approach...),
- eliminate the UB-dangerous code, or make it opt-in,
- rework the tutorial portion of the docs, and
- many assorted doc cleanups.
That does not seem like an unreasonable amount of stuff to resolve in
post-review acceptance readiness.
I bring all this up because I do in fact hope that JeanHeyd brings this
back for a future review, and I hope that my arguments here will convince
some of you that this library is a useful thing to have around.
I waited until now to say any of these things in an attempt to remain
impartial during the review; I've been bothered by seeing previous review
managers clearly represent a pro-acceptance position from the start.
Thanks to JeanHeyd for his submission. A no-vote from this group is a
tough thing, and I hope that the feedback he has received takes some of the
sting out.
Zach