From: JeanHeyd Meneide (phdofthehouse_at_[hidden])
Date: 2019-06-30 04:31:17
Dear Andrzej Krzemienski,
Scaling, in this terms, means with developer time. More comments below:
I must admit, I do not understand this argumentation. No destructor to
> write, but instead a deleter to write. At least destructor is in one place
> with the constructor.
> Of course, if somebody later comes in, and starts adding members and
> functionality to this class it will get broked. C++ does not prevent users
> from conciously dong nasty things. But the point of such class would be to
> only manage a single resource. With this single responsibility in mind
> no-one will be adding function calls to the class to "improve" it.
There isn't any special code required to get to a nasty state: glGenBuffers
sets errors that you must check with glGetError(). There's no way to
communicate the return of glGetError() out of the destructor without
either out parameters (std::file_system style) or throwing exceptions. The
former is not the greatest design choice: the latter results in exactly the
problems I defined with Phil Endecott's approach. out_ptr does not make
throwing exceptions undesirable or require manual cleanup in the
constructor: the cleanup goes once inside of the unique_ptr, once inside of
the deleter, and never needs to be wrapped or written again.
> > There are similar problems with your PNG example.
> > All of this goes to show that while you can write cute one-off
> > examples that seem simpler, they do not scale to the fundamental reality
> > that is handing resources, dealing with exceptions, and more in C++ APIs.
> What "scaling" do you mean here? I have seen one example of changing from
> managing one resource to managing a list of resources, which does not fit
> into my definition of "scaling". Building classes that have a single
> responsibility of managing a single resource does address the problem of
> dealing with exceptions and early returns. Also, I would like to know what
> you mean by "and more" in this claim.
The problem with the "wrap it in a class" or "write my own wrapper
functions" is two-fold: it only works for that one bit of wrapped
functionality, especially if the parameters are hardcoded. It becomes hard
to extend and expose that functionality, while still maintaining exception
safety and proper RAII. It is also fundamentally impossible to scale said
wrappers: even if you manage to make a good wrapper for OpenGL, the
structure of another library behaves rather differently. Even if the
original example, we saw 2 different C libraries which handled their
structure generation and error handling differently: one uses a
GetLastError style of handling, and the other (libpng) has function
pointers for errors. Writing a wrapper or transforming that into a class
with a single responsibility that encapsulates all of the safety and
necessary speed without leaking in the presence of exceptions or
longjmp/setjmp error buffer handling or callbacks is difficult. out_ptr is
successful because it does not try to wrap everything: it focuses on doing
one thing -- safety of initialized values -- very well. That's why multiple
companies with large C legacies have used it with the already enormous
success that are smart pointers. And that's why other companies and
individuals who have read the paper and seen it during the reviews have
been very supportive of the proposal and the implementation.
> Do you mean by "scaling" changing for m propagating a single resource to
> propagting a list of resources? How often do you do that? (Given that you
> have changed the type and now you have to change every place that uses the
I should have been specific: I mean scaling with developer time. It is
infinitely easier to reuse smart pointers designed for the task of resource
management and making them play nice with existing C APIs then wrapping
every single C API call you need to deal with, OpenGL or DirectX or Python
C API or otherwise. This is not a conjecture: I spent 2 years wrapping
OpenGL and DirectX APIs by-hand to create the perfect C++ interface. But it
was brittle, it didn't apply to other C APIs which chose different
mechanisms of error handling or resource allocation, and further problems.
Instead of wrapping, I instead focused on safety and simply making the C
API easier to use after Mark Boyall taught me about out_ptr. This resulted
in much of my time spent doing what I was supposed to be doing -- graphics
and simulations -- rather than meticulously picking at every API and trying
to wring a good C++ object or interface out of it.
In short: gentle, non-intrusive abstractions provided greater velocity for
focusing effort where it mattered, and not having to piddle with details or
accidentally leak resources when trying to use the default C++ error
handling mechanism that is exceptions.
> A small degree of additional templating
> Well, "additional templating" doesn't sound as something good.
> -- if
> > you have the time and skill for it --
> What skills are required here?
Skill as in architecting a solution that works further beyond what I have
shown here, keeping smart pointers to do the job of handling lifetime and
having easy-to-use deleters which wrap additional parameters (for example,
the size passed to a glGenBuffers call). I have written such systems before
and they performed and worked well, but I think building such a thing in
the middle of this mailing list might be a lot more effort than it is
worth. That's not to say other people are skill-less: just trying to make
such a layer as reusable and fast as possible takes some time and effort
that's not exactly trivial but pays dividends in code reuse.
All my best,
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk