Is there a proper 'ownership-in-a-package' for 'handles' available?
Asked Answered
S

2

5

Handles have proper semantics other than pointers. So for me an example like this (extracted from the Rule of Zero):

class module {
public:
    explicit module(std::wstring const& name)
    : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

    // other module related functions go here

private:
    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

    module_handle handle;
};

using unique_ptr as an 'ownership-in-a-package' for handles is a bad example. First, it makes use of internal knowledge that the handle is a pointer type, and use this to make a unique_ptr to the basic type the "opaque" handle type builds upon.

Handles can be any type, they may be a pointer, they may be an index or who knows. Most importantly, what you have at hand (from most C API's for example) is a handle and its resource releasing function.

Is there a proper 'ownership-in-a-package' that works in handle semantics? I mean, already publicly available for one to use?

For me, unique_ptr et. al. doesn't work, I must make unnecessary assumptions about what the handle type is, when what I want is just to get an 'ownership-in-a-package' through the opaque handle type and its releasing function, solely.

It doesn't make sense for one to peer inside the handle type to make constructions upon this information. It's a handle, it should not matter.

I'll quote here the feelings of another SO user in another question's answer:

Create a specific "smart pointer" class, won't take long. Don't abuse library classes. Handle semantics is quite different from that of a C++ pointer; for one thing, dereferencing a HANDLE makes no sense.

One more reason to use a custom smart handle class - NULL does not always mean an empty handle. Sometimes it's INVALID_HANDLE_VALUE, which is not the same.

Disclaimer:

This question reformulates and builds upon this one:

Sphacelus answered 14/2, 2013 at 15:22 Comment(8)
I disagree with your premise. unique_ptr is maybe a slight misnomer – it handles resources. A HANDLE also handles resources. This is a perfect match.Stutter
I don't base my premises solely on the type name...Sphacelus
What is your problem, exactly? You don't like the name unique_ptr?Lat
@EtiennedeMartel: Well, it does also take a T* at construction.Bullate
@EtiennedeMartel: To use unique_ptr for a non-pointer handle, you need to a special deleter which defines a nested pointer type, and that pointer type can't simply be, say, int, since int doesn't conform to the NullablePointer requirements that unique_ptr wants. However, you can write a simple wrapper that adapts anything to those nullable pointer requirements. If you fancy, you can also add a unary operator* that forwards to *value, and an operator-> so you can actually use *up and up->foo().Danielson
@Xeo, that appears to be the answer chico is asking for -- your wrapper, together with a bit of template wrapping around unique_ptr.Gardening
@Xeo: If you're going to go through the trouble of writing that wrapper... why not just write a proper RAII object that can hold any value and call a given function/functor on that type when it is destroyed? It's a hell of a lot less obtuse than using unique_ptr for non-pointer things. Seriously, writing copy/move constructors for one object is hardly an onerous burden.Reich
In one sense, this question should probably have been closed because asking for libraries and other resources is off-topic and prone to opinionated answers.Sanjuanitasank
S
0

std::experimental::unique_resource

Sphacelus answered 15/2, 2013 at 20:35 Comment(6)
Why force the client to manually provide cleanup (i.e. the lambda passed to make_scoped_resource) at the usage locations?Salientian
@R.MartinhoFernandes Isn't it the case for unique_ptr with deleter? And if use default_deleter in handle semantics does it make sense? I guess in most cases you get shorter syntax with that, and you have the std::unique_resource option that won't force that. But still, I wonder about a good solution for this.Sphacelus
Not if your deleter is default-constructible. The point is that make_scoped_resource, just like unique_ptr's ctor that takes a deleter, should never be used in client code. It should only be used in library functions. Something like make_gl_list, that internally perhaps uses make_scoped_resource, but doesn't force the client to care about it.Salientian
@R.MartinhoFernandes Why force a language user to wrap itself all situations involving handles in legacy API usage? A library function may need to call a legacy API once for some operation, why force wrapping for such trivial situation? It's common usage for people interfacing with C, why not provide good resources to deal with this? Little wrappers, private similar functionality wrapper libraries, code tricks, all this must go!Sphacelus
What. In your code sample the user itself must deal with the legacy API (see the call to glDeleteLists there?). That is what should go away.Salientian
@R.MartinhoFernandes ?. of course I'm talking about dealing with the legacy API. What I don't like is bad practices (unique_ptr<void, deleter>), or dealing with the language itself getting in my way for this.Sphacelus
B
2

The type unique_ptr is less general than the phrase "handle", yes. But why shouldn't it be? Just one of your "handle" examples (say, the one that is an integer index), is precisely as general as unique_ptr. You can't compare one specific kind of handle with "all handles ever".

If you want a single, concrete C++ type (or type template) that is a handle without actually defining any specific handling semantics then... I can't help you. I don't think anyone tractibly could.

Bullate answered 14/2, 2013 at 15:30 Comment(11)
It's less general, and hence, should be used where it fits. When one starts looking up what HANDLE (supposed to be opaque, one of the main characteristics of handles) is built upon, and use that information in the less general tool, for me, this is bad smell. Of course, one can assert HANDLE is void * for my case, what about other handle based libraries, are you going to assume that assertion aways? And this assumption is not part of handle semantics, HANDLE is opaque.Sphacelus
@chico: "other handle based libraries" have different handle types. unique_ptr is one handle type. What actual problem are you encountering in your coding?Bullate
unique_ptr is one handle type? I dunno what you mean by that. HANDLE is a handle type. My actual problem is ignoring it's opaque to make use of unique_ptr to manage the lifetime of the resource it refers to. I consider this bad practice, and I'm asking for a known 'ownership-in-a-package' alternative that does it right. By right I mean, not violation encapsulation like that. Handle types are opaque and a good tool would handle handles, at last integral and pointer based, the same syntactical way.Sphacelus
@chico: You keep saying that handle types must be opaque but I don't see any reason for it. unique_ptr just does fine for a handle with pointer semantics. Sure, it doesn't have non-pointer semantics. It's one kind of handle.Bullate
It doesn't matter whether one doesn't see a reason for handles to be assumed as opaque, what matters is that they're generally assumed as such. You may build your handle type as a void * internally and assert upfront for your customers it'll aways be like that. Your clients are than safe to use unique_ptr<void> with it (look that unique_ptr<void> doesn't really make sense, you can infer nothing from a handle type, it's not a pointer to untyped). But this is the case for your special handle. This process doesn't fit for handles, generally speaking.Sphacelus
It breaks encapsulation, it forces you to ask what HANDLE (et.al.) is built upon, where I was aways taught to assume handles as opaque.Sphacelus
You can use unique_ptr for opaque handles or even completely non-pointer types, like POSIX file descriptors.Triptolemus
@DeadMG And functors can even save state, but they are not lambdas.Sphacelus
@chico: C++ lambda expressions aren't really lambdas, either. They are anonymous functors.Bullate
@LightnessRacesinOrbit, as you also have anonymous classes, but still, no one is calling for special syntax for them. The fact is, the language now, have a special syntax for such anonymous functors. They're handy to have. And they made their way into the language. They're not cumbersome to write.Sphacelus
@chico: I never said that they aren't, or that they didn't, or that they are.Bullate
S
0

std::experimental::unique_resource

Sphacelus answered 15/2, 2013 at 20:35 Comment(6)
Why force the client to manually provide cleanup (i.e. the lambda passed to make_scoped_resource) at the usage locations?Salientian
@R.MartinhoFernandes Isn't it the case for unique_ptr with deleter? And if use default_deleter in handle semantics does it make sense? I guess in most cases you get shorter syntax with that, and you have the std::unique_resource option that won't force that. But still, I wonder about a good solution for this.Sphacelus
Not if your deleter is default-constructible. The point is that make_scoped_resource, just like unique_ptr's ctor that takes a deleter, should never be used in client code. It should only be used in library functions. Something like make_gl_list, that internally perhaps uses make_scoped_resource, but doesn't force the client to care about it.Salientian
@R.MartinhoFernandes Why force a language user to wrap itself all situations involving handles in legacy API usage? A library function may need to call a legacy API once for some operation, why force wrapping for such trivial situation? It's common usage for people interfacing with C, why not provide good resources to deal with this? Little wrappers, private similar functionality wrapper libraries, code tricks, all this must go!Sphacelus
What. In your code sample the user itself must deal with the legacy API (see the call to glDeleteLists there?). That is what should go away.Salientian
@R.MartinhoFernandes ?. of course I'm talking about dealing with the legacy API. What I don't like is bad practices (unique_ptr<void, deleter>), or dealing with the language itself getting in my way for this.Sphacelus

© 2022 - 2024 — McMap. All rights reserved.