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:

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
oblitum
  • 11,380
  • 6
  • 54
  • 120
  • 2
    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. – Konrad Rudolph Feb 14 '13 at 15:27
  • 1
    I don't base my premises solely on the type name... – oblitum Feb 14 '13 at 15:29
  • 1
    What _is_ your problem, exactly? You don't like the name `unique_ptr`? – Etienne de Martel Feb 14 '13 at 15:32
  • @EtiennedeMartel: Well, it does also take a `T*` at construction. – Lightness Races in Orbit Feb 14 '13 at 15:33
  • 5
    @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](http://stacked-crooked.com/view?id=1d4c6531a2c7b0d64b9cadd3dd14f9d2) 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()`. – Xeo Feb 14 '13 at 15:34
  • @Xeo, that appears to be the answer chico is asking for -- your wrapper, together with a bit of `template` wrapping around `unique_ptr`. – Yakk - Adam Nevraumont Feb 14 '13 at 16:06
  • 5
    @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. – Nicol Bolas Feb 15 '13 at 03:23
  • 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. – Adrian McCarthy Aug 28 '20 at 14:31

2 Answers2

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.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 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. – oblitum Feb 14 '13 at 15:52
  • @chico: "other handle based libraries" have different handle types. `unique_ptr` is _one_ handle type. What _actual_ problem are you encountering in your coding? – Lightness Races in Orbit Feb 14 '13 at 16:09
  • 2
    `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. – oblitum Feb 14 '13 at 17:37
  • @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. – Lightness Races in Orbit Feb 14 '13 at 17:42
  • 1
    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` with it (look that `unique_ptr` 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. – oblitum Feb 14 '13 at 17:50
  • 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. – oblitum Feb 14 '13 at 17:55
  • You can use unique_ptr for opaque handles or even completely non-pointer types, like POSIX file descriptors. – Puppy Feb 16 '13 at 16:17
  • @DeadMG And functors can even save state, but they are not lambdas. – oblitum Feb 17 '13 at 20:38
  • @chico: C++ lambda expressions aren't _really_ lambdas, either. They are anonymous functors. – Lightness Races in Orbit Feb 17 '13 at 20:46
  • @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. – oblitum Feb 17 '13 at 21:02
  • @chico: I never said that they aren't, or that they didn't, or that they are. – Lightness Races in Orbit Feb 17 '13 at 21:03
0

std::experimental::unique_resource

oblitum
  • 11,380
  • 6
  • 54
  • 120
  • 1
    Why force the client to manually provide cleanup (i.e. the lambda passed to `make_scoped_resource`) at the usage locations? – R. Martinho Fernandes Feb 16 '13 at 16:22
  • @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. – oblitum Feb 16 '13 at 16:53
  • 2
    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. – R. Martinho Fernandes Feb 16 '13 at 16:55
  • @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! – oblitum Feb 16 '13 at 17:14
  • 1
    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. – R. Martinho Fernandes Feb 16 '13 at 17:33
  • @R.MartinhoFernandes ?. of course I'm talking about dealing with the legacy API. What I don't like is bad practices (unique_ptr), or dealing with the language itself _getting in my way_ for this. – oblitum Feb 16 '13 at 17:39