On Thu, Feb 15, 2018 at 8:10 AM, Richard Hodges via Boost
The idea is correct, but the implementation is a little naiive IMHO.
Thanks for taking the time to check it out!
I recently wrote a very similar thing for abstracting OpenGL object handles and file handles. Brook's comment is correct. You'll need some kind of policy class to handle:
1. creation 2. destruction 3. testing for an invalid handle (not always zero)
unique_val allows for a compile-time default value. Of course, this is of no use if testing for an invalid handle requires more complex logic. But please see below.
4. move/move-construct 5. (possibly) handle duplication upon copy (some handles, e.g. FDs allow this) 6. preventing the moving of one kind of handle into another (e.g. an OpenGL VertexArray is a GLuint and so is a ShaderProgram, but they are not conceptually compatible)
Also, some resources can be created in groups (see glGenVertexArrays). This argues for the idea of an array/vector version, or at least a method on the policy class for vector-like construction.
Agreed, an array version similar to the way std::unique_ptr does it is a good idea!
To me, a more expressive name might be:
template<class HandleService> struct unique_handle;
Where HandleService defines the service/policy class with services such as:
using native_handle_type =
; auto construct(...args) -> native_handle_type; void destroy(native_handle_type&) noexcept; bool empty(native_handle_type const&) noexcept; auto move_construct(native_handle_type&& from) noexcept -> native_handle_type; auto move_assign(native_handle_type&& from, native_handle_type& to) noexcept -> void;
// with option dup/copy methods if the underlying handle supports them auto copy_or_duplicate(native_handle const&) -> native_handle;
I can see the value in having a class with all the required customization points to build a RAII class right out of it. However, shouldn't that be a separate class (indeed, possibly called unique_handle) that might use (or not) a unique_val internally to store the actual raw handle? In other words, the point of unique_val is isolating the concept of a unique value, rather than trying to be a completely generic RAII template with all the required hooks to build handle classes -- even if unique_handle can be made to reduce to unique_val with a default policy. In other words, I don't see many people using unique_handle to replace their custom RAII classes; while I can easily see people picking up unique_val here and there, given its simplicity. Also, there is the following argument that could be made: if you have to write such a complex HandleService, you are probably better off writing directly the RAII class instead; in the end, you are not gaining much by using unique_handle since all the actual work is actually in the HandlerService. While in this context, unique_val only takes care of ensuring the value is move-only (so your RAII class is already noncopyable), giving you move semantics with a proper default value (so the right thing already happens) and a nice name to mark your raw value with; while at the same time being extremely simple to understand and apply by anyone. Please, do not get me wrong, I am not trying to argue against other possibilities and I am no expert on writing generic libraries for other people to use! Actually the custom deleter, the array version and the shared idea are all very nice improvements in my opinion, given that they do not add complexity for their typical usage and it is already well-known from their parallel in std::unique_ptr (shared is not complex in the sense that it would be another class).
Further: It is possible that handles exist within an outer context, such as a resource cache with automatic lifetime management. In this case the HandleService would need to be a value carried in the unique_handle, so it can track the state of the outer container.
Therefore the HandleService class in most cases will be an empty functor, but could also hold a reference to container state.
Further Again:
unique_handle might have a method on it called share() which returns a shared_handle<HandleService>. Much like std::future::share.
This is also an interesting proposal. Being able to use the reference counting features of shared_ptr outside of pointers makes sense. Thanks! Miguel
On 14 February 2018 at 21:18, Miguel Ojeda via Boost
wrote: Hi all,
While working on a medium-sized project, I had to wrap quite some handles, IDs, descriptors, etc. into RAII classes. Somehow, writing those and making them movable seemed more verbose than needed. I searched in the STL and Boost for something already implemented, but nothing seemed to come up. I decided to write my own prototype to see whether I was missing something important; but actually using it in my project felt quite natural and simple.
At this point, I asked for a second opinion from a well-known expert (thanks Scott!) and he agreed that it seemed like a reasonable idea but, of course, he would also be surprised if others haven't come up with something similar. So I polished it up a bit and uploaded it to:
https://github.com/ojeda/unique_val
Below you have the introduction inlined [1].
If you have the time, please take a look (specially to the Rationale) and let me know your comments. The code itself is very short and straightforward.
If you already have this class/template in Boost somewhere that I missed, please let me know! Otherwise, if you think this could be interesting to put into Boost in some existing library (not sure which) or as a new tiny library, I would be glad to make the effort to expand it, clean it up, etc.
Thank you! Miguel
[1]
""" A single-header header-only library for representing unique values (handles, IDs, descriptors...) that default to a value on move for C++11, C++14 and C++17.
It simplifies writing resource-owning classes (RAII) with move semantics support for resources with handles, IDs or descriptors that are supposed to be used as unique values, not as unique pointers. For instance, it can be used with non-pointer handlers (like the `int` file/socket descriptors in POSIX) and with opaque pointer handlers (like the `HWND` handles in the Win32 API).
As such, it can be used as an alternative to `std::unique_ptr` with or without custom deleter, specially for resources that use a non-pointer type or resources that are not simply heap-allocated.
It does not have any overhead compared to using the original type. It supports booleans, integers, pointers and enumerations. The default value can be different than the default-constructed value (i.e. different than 0, nullptr, etc.). """
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost