Hi Everyone, This is not a review of out_ptr, but I wanted to share my thoughts about the motivation for this library. I do not recall any time from my programming experience where I was in need of passing a smart pointer to a T** interface, so maybe this is why I cannot appreciate the value of out_ptr. On the other hand, if I need to use a library with a C API, or other non-C++ API (this happens when someone wants to naively port a Java library to C++ and keep the Java interface) the first thing I do is to wrap the API functions into a RAII class. It takes some time up front, but then whenever it is used, I do not have to deal with the problems of the old API. `T**` is only one of the problems. The motivation focuses on only easily passing smart pointers to `init(T**)` functions, but it does not address the problem of adapting the error reporting of `init(T**)`to the error reporting of my program. I would never want to only solve the former problem and omit the latter. Also, I would never want to represent a handle to a resource with a smart pointer, as I do not want to voluntarily inject the null-pointer state that I now have to take care of everywhere. Instead I would prefer a dedicated type that is not default-constructible, where one really needs to try hard to get the special no-resource state (e.g. by moving from and not destroying the object). What I am missing from the motivation of out_ptr is the comparison with a full RAII wrapper solution. You can argue that the scope of out_ptr is just different: provide interpoperation between `init(T**)`functions and smart pointers. But I would argue that this latter goal is wrong. In fact, I am concerned that we might be encouraging a bad programming style: "need to work with init(T**) APIs? use shared_ptr." instead of promoting RAII wrappers that directly wrap the resource. Does anyone else feel the same? Or am I missing something about the problem domain? Anyway, I think that this problem should be covered in the documentation. Regards, &rzej;
I feel like I am in your boat! Regards Peter. Andrzej Krzemienski via Boost wrote on 28.06.19 10:08:
Hi Everyone, This is not a review of out_ptr, but I wanted to share my thoughts about the motivation for this library. I do not recall any time from my programming experience where I was in need of passing a smart pointer to a T** interface, so maybe this is why I cannot appreciate the value of out_ptr.
On the other hand, if I need to use a library with a C API, or other non-C++ API (this happens when someone wants to naively port a Java library to C++ and keep the Java interface) the first thing I do is to wrap the API functions into a RAII class. It takes some time up front, but then whenever it is used, I do not have to deal with the problems of the old API. `T**` is only one of the problems.
The motivation focuses on only easily passing smart pointers to `init(T**)` functions, but it does not address the problem of adapting the error reporting of `init(T**)`to the error reporting of my program. I would never want to only solve the former problem and omit the latter. Also, I would never want to represent a handle to a resource with a smart pointer, as I do not want to voluntarily inject the null-pointer state that I now have to take care of everywhere. Instead I would prefer a dedicated type that is not default-constructible, where one really needs to try hard to get the special no-resource state (e.g. by moving from and not destroying the object).
What I am missing from the motivation of out_ptr is the comparison with a full RAII wrapper solution. You can argue that the scope of out_ptr is just different: provide interpoperation between `init(T**)`functions and smart pointers. But I would argue that this latter goal is wrong. In fact, I am concerned that we might be encouraging a bad programming style: "need to work with init(T**) APIs? use shared_ptr." instead of promoting RAII wrappers that directly wrap the resource.
Does anyone else feel the same? Or am I missing something about the problem domain? Anyway, I think that this problem should be covered in the documentation.
Regards, &rzej;
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 6/28/19 11:08 AM, Andrzej Krzemienski via Boost wrote:
Hi Everyone, This is not a review of out_ptr, but I wanted to share my thoughts about the motivation for this library. I do not recall any time from my programming experience where I was in need of passing a smart pointer to a T** interface, so maybe this is why I cannot appreciate the value of out_ptr.
On the other hand, if I need to use a library with a C API, or other non-C++ API (this happens when someone wants to naively port a Java library to C++ and keep the Java interface) the first thing I do is to wrap the API functions into a RAII class. It takes some time up front, but then whenever it is used, I do not have to deal with the problems of the old API. `T**` is only one of the problems.
The motivation focuses on only easily passing smart pointers to `init(T**)` functions, but it does not address the problem of adapting the error reporting of `init(T**)`to the error reporting of my program. I would never want to only solve the former problem and omit the latter. Also, I would never want to represent a handle to a resource with a smart pointer, as I do not want to voluntarily inject the null-pointer state that I now have to take care of everywhere. Instead I would prefer a dedicated type that is not default-constructible, where one really needs to try hard to get the special no-resource state (e.g. by moving from and not destroying the object).
What I am missing from the motivation of out_ptr is the comparison with a full RAII wrapper solution. You can argue that the scope of out_ptr is just different: provide interpoperation between `init(T**)`functions and smart pointers. But I would argue that this latter goal is wrong. In fact, I am concerned that we might be encouraging a bad programming style: "need to work with init(T**) APIs? use shared_ptr." instead of promoting RAII wrappers that directly wrap the resource.
Does anyone else feel the same? Or am I missing something about the problem domain? Anyway, I think that this problem should be covered in the documentation.
In my experience, a full hand-rolled RAII wrapper is an overkill in many, if not most cases. Most C-style APIs follow the new/delete pattern, where you have a function that allocates and initializes a structure, returning a pointer to it, and another one to free it, along with any associated resources. Using a smart pointer to store a pointer to that structure is perfectly valid, and the easiest and most natural thing to do. Granted, Boost.OutPtr is aimed at only a subset of these APIs, which return the allocated pointer via an out parameter, not a function return value. There are other examples beyond new/delete pattern, like COM, for instance, which has the convention to return pointers to interfaces in the out parameters (because the return type is reserved for HRESULT). I'm sure there are other APIs that follow a similar convention. The important thing to note here is that the returned pointer represents exactly that - a pointer to something. And using a smart pointer to manage that resource is perfectly adequate. Boost.OutPtr tries to simplify the transition from the raw C pointer form to the smart pointer form and make it safer. Also, I think adapting error reporting mechanism is an entirely different task. You may implement it as part of the same component (e.g. a wrapper around the library), but it is still a separate thing to do, unrelated to RAII and pointer adaptation.
Dear Andrzej Krzemienski, Wrapping all of the used APIs -- and maintaining those wrappers for any long-term, sustained period of time -- is not a feasible task or architectural burden for many developers and their codebases. Transforming error returns + initialization sequences is a subject best left to an entirely separate library, possibly using the Reflection TS and (maybe) some form of Herb Sutter's proposed metaclasses idea. Doing that in a generic way in today's C++ without these facilities is incredibly difficult and likely to produce unwieldy boilerplate or a large number of specially hand-crafted functions. Finally, out_ptr works with far more than just pointers. The specification and implementation has out-of-the-box support for plenty of RAII types, including retain_ptr/intrusive_ptr, unique_resource, my own handle type (as shown in the example code), and more. In the San Diego C++ meeting LEWG was very pointed in asking whether this works for just pointers or a large class of types: the design very deliberately works with most pointer types. (Comparisons to nullptr are part of that deal, but nothing requires nullptr initialization: the specification specifically uses "it's value initalization" when it needs to create an empty Pointer object.) Sincerely, JeanHeyd
pt., 28 cze 2019 o 10:59 JeanHeyd Meneide via Boost
Dear Andrzej Krzemienski,
Wrapping all of the used APIs -- and maintaining those wrappers for any long-term, sustained period of time -- is not a feasible task or architectural burden for many developers and their codebases.
Yes, maybe this is where I fail to understand the constraints.
Transforming error returns + initialization sequences is a subject best left to an entirely separate library, possibly using the Reflection TS and (maybe) some form of Herb Sutter's proposed metaclasses idea. Doing that in a generic way in today's C++ without these facilities is incredibly difficult and likely to produce unwieldy boilerplate or a large number of specially hand-crafted functions.
I do not think anyone would like to handle this with a library. Every API is different, and you need manual inspection to understand what it does.
Finally, out_ptr works with far more than just pointers. The specification and implementation has out-of-the-box support for plenty of RAII types, including retain_ptr/intrusive_ptr, unique_resource, my own handle type (as shown in the example code), and more. In the San Diego C++ meeting LEWG was very pointed in asking whether this works for just pointers or a large class of types: the design very deliberately works with most pointer types. (Comparisons to nullptr are part of that deal, but nothing requires nullptr initialization: the specification specifically uses "it's value initalization" when it needs to create an empty Pointer object.)
If I am passing my smart pointer by reference to out_otr(), I do not see why you would need to assign a value-initialized value to it. Maybe what would be helpful is a concept that specifies the requirements on parameter `Smart` so that we would figure out easily what can and what cannot be passed to `out_ptr()`? Regards, &rzej;
Sincerely, JeanHeyd
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On Fri, 28 Jun 2019 at 11:38, Andrey Semashev via Boost < boost@lists.boost.org> wrote:
Most C-style APIs follow the new/delete pattern, where you have a function that allocates and initializes a structure, returning a pointer to it, and another one to free it, along with any associated resources.
Doesn't sound like overkill to me, now I have no dependency and I can add details at will [without reading any docs].
Using a smart pointer to store a pointer to that structure is perfectly valid, and the easiest and most natural thing to do.
Right, most natural since C++11 that is then.
And using a smart pointer to manage that resource is perfectly adequate.
It's the "if-I-have-a-hammer-everything-looks-like-a-nail-syndrom". degski -- @realdegski https://edition.cnn.com/interactive/2019/06/middleeast/saudi-teen-death-pena... "Anyone who believes that exponential growth can go on forever in a finite world is either a madman or an economist" - Kenneth E. Boulding
On Fri, Jun 28, 2019 at 1:08 PM degski
On Fri, 28 Jun 2019 at 11:38, Andrey Semashev via Boost
wrote: Most C-style APIs follow the new/delete pattern, where you have a function that allocates and initializes a structure, returning a pointer to it, and another one to free it, along with any associated resources.
Doesn't sound like overkill to me, now I have no dependency and I can add details at will [without reading any docs].
Using a smart pointer to store a pointer to that structure is perfectly valid, and the easiest and most natural thing to do.
Right, most natural since C++11 that is then.
And using a smart pointer to manage that resource is perfectly adequate.
It's the "if-I-have-a-hammer-everything-looks-like-a-nail-syndrom".
Maybe, but I'm definitely not looking forward wrapping something like AVCodecContext in a custom RAII wrapper, when I can just use unique_ptr with a deleter.
On 28.06.19 10:08, Andrzej Krzemienski via Boost wrote:
What I am missing from the motivation of out_ptr is the comparison with a full RAII wrapper solution. You can argue that the scope of out_ptr is just different: provide interpoperation between `init(T**)`functions and smart pointers. But I would argue that this latter goal is wrong. In fact, I am concerned that we might be encouraging a bad programming style: "need to work with init(T**) APIs? use shared_ptr." instead of promoting RAII wrappers that directly wrap the resource.
It seems to me that out_ptr is a useful tool for writing such wrapper.
class my_wrapper {
public:
my_wrapper() {
if (acquire_my_c_resource(out_ptr(this->p))) {
handle_error();
}
}
// No need to muck about with destructor or copy/move constructors
private:
std::unique_ptr
pt., 28 cze 2019 o 12:09 Rainer Deyke via Boost
On 28.06.19 10:08, Andrzej Krzemienski via Boost wrote:
What I am missing from the motivation of out_ptr is the comparison with a full RAII wrapper solution. You can argue that the scope of out_ptr is just different: provide interpoperation between `init(T**)`functions and smart pointers. But I would argue that this latter goal is wrong. In fact, I am concerned that we might be encouraging a bad programming style: "need to work with init(T**) APIs? use shared_ptr." instead of promoting RAII wrappers that directly wrap the resource.
It seems to me that out_ptr is a useful tool for writing such wrapper.
class my_wrapper { public: my_wrapper() { if (acquire_my_c_resource(out_ptr(this->p))) { handle_error(); } } // No need to muck about with destructor or copy/move constructors private: std::unique_ptr
p{nullptr, release_my_c_resource}; }
Technically true, but now the advantage of out_ptr seems so minimal that the costs of including it or reading the documentaiton outweigh the benefits. Regards, &rzej;
-- Rainer Deyke (rainerd@eldwood.com)
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
participants (6)
-
Andrey Semashev
-
Andrzej Krzemienski
-
degski
-
JeanHeyd Meneide
-
Peter Sommerlad
-
Rainer Deyke