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 code.)
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, JeanHeyd