You can certainly achieve the goal with an overload of operator&
as others have mentioned, but that's fraught with risk that defeats the purpose of using the wrapper in the first place.
Consider how you might use a hypothetical HandleWrapper with a simple overload of operator&
that just returns a pointer to the internal handle:
HandleWrapper<KernelHandleTraits> handle;
if (CreateFile(&handle, "first", ...) == true) {
// process the first file...
}
if (CreateFile(&handle, "second", ...) == true) {
// process the second file...
}
This leaks the handle to the first file, because the operator&
overload lets you do a backdoor assignment. Note that the wrapper class doesn't have an assignment operator specifically so that it doesn't have to cope with the complications of re-using a wrapper to track a second resource once you're done with the first.
You could extend the wrapper to do something smarter if it already contains a resource but the class gets more complex and you get farther from the idea of RAII.
If we're splitting hairs, the HandleWrapper code referenced applies RRID (resource release is destruction) rather than RAII (resource allocation is initialization).
- An RRID class is a wrapper you can transfer ownership of an already allocated resource to (in order to ensure that it's released later).
- An RAII class is one whose lifetime is exactly the same as the resource it wraps. It owns the resource the moment it comes into existence and dies when the resource is released.
If you have a function that returns resources as "output parameters," you can write an adapter function:
HANDLE CreateFileAdapter(const char * filename, ...) {
HANDLE handle;
return CreateFile(&handle, "filename", ...) ? handle : INVALID_HANDLE_VALUE;
}
You can then use your HandleWrapper as-is by calling the adapter function:
HandleWrapper<KernelHandleTraits> handle(CreateFileAdapter("filename", ...));
A strict RAII solution would encapsulate the allocation of the resource in a constructor just as it encapsulates the deallocation in its destructor. You can build an RAII wrapper on the back of an RRID wrapper and an adapter function:
class FileHandleWrapper : public HandleWrapper<KernelHandleTraits> {
public:
FileHandleWrapper(const char * file_name, ...) :
HandleWrapper(CreateFileAdapter(file_name, ...) {}
private:
static HANDLE CreateFileAdapter(const char *file_name, ...) {
HANDLE handle;
return CreateFile(&handle, "filename", ...) ?
handle : INVALID_HANDLE_VALUE;
}
};
Note that I've made the adapter function a private static, to ensure that nobody uses it in an unsafe manner.