Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have no idea what this C++ one-liner does

Tags:

c++

I found on Github a function with a pretty weird one-liner inside:

std::unique_ptr<std::remove_pointer<HANDLE>::type, void(*)(HANDLE)> hDevice{h, [](HANDLE handle){CloseHandle(handle);}};

As a person who never dealt with c++ I have no idea what it does.

I suppose, here are two nested anonymous functions connected somehow with std::remove_pointer and std::unique_ptr calls. I see an WinAPI CloseHandle call in the inner function and suppose that I should start my analysis with it. I cannot say more.

May be here are two nested generics.

The use of curly braces and a "bigger than" symbol also seems pretty strange to me. It breaks all the canonical cases I know.

Please help to understand this conglomerate. I do not know how to google its parts.

like image 530
Paul Avatar asked Jul 30 '19 13:07

Paul


1 Answers

First let's format it so it's readable:

std::unique_ptr<std::remove_pointer<HANDLE>::type, void(*)(HANDLE)> hDevice{
   h,
   [](HANDLE handle) { CloseHandle(handle); }
};

This is a declaration of an object called hDevice, initialised with two arguments. One is h and the other is a lambda function. We'll come back to that.

The object's type is std::unique_ptr<std::remove_pointer<HANDLE>::type, void(*)(HANDLE)>. The <> mean that this is an instantiation of a template. The template in question is std::unique_ptr, a standard component for memory management, a smart pointer.

Two things:

  • std::unique_ptr<T> is a type of pointer to objects of type T.
  • std::unique_ptr<T, D> is a type of pointer to objects of type T with custom "deleter" of type D; we'll come back to that.

Your T is std::remove_pointer<HANDLE>::type. Windows gives us the type alias HANDLE which is an obfuscated pointer type. This expression gives us the type of the thing that a HANDLE points to (and does so in a nice generic way that doesn't have to hardcode the result; Microsoft could change the definition of HANDLE and, as long as it is still a pointer, this'll still work).

Your D is void(*)(HANDLE). That's the type of a function pointer, a pointer to a function taking HANDLE and returning void. Oh, that looks familiar: it's a function pointer type compatible with the lambda you gave as the constructor parameter.

That's because the lambda is the custom deleter. It tells the unique_ptr what to do when the smart pointer goes out of scope. Usually that'd just be a nice delete, but here we have a Windows API function (CloseHandle) to call instead, which does the cleanup for us. This may involve a delete and other things, or it may involve just other things. Point is this is the proper way to close a HANDLE.

Overall, what's going on is that this declaration creates a std::unique_ptr that takes ownership of a HANDLE h, and ensures that CloseHandle(h) is invoked when the std::unique_ptr goes out of scope.

tl;dr: It's a way to add RAII to a Windows handle.

like image 151
Lightness Races in Orbit Avatar answered Nov 07 '22 02:11

Lightness Races in Orbit