📜 ⬆️ ⬇️

Using traits for Win32 API

Introduction


Everyone knows that the Win32 API is not very user friendly. For example, to call a function, you often have to fill a large structure with fields like cbSize , dwFlags , etc. Or, to get a string, first find out its size, prepare a buffer and then only get the string itself. Then we will talk about the function ::HttpQueryInfo() and the use of the idiom "traits" to simplify working with it.

Rectilinear way


Code taken from MSDN :
 // Retrieving Headers Using a Constant BOOL SampleCodeOne(HINTERNET hHttp) { LPVOID lpOutBuffer=NULL; DWORD dwSize = 0; retry: // This call will fail on the first pass, because // no buffer is allocated. if(!HttpQueryInfo(hHttp,HTTP_QUERY_RAW_HEADERS_CRLF, (LPVOID)lpOutBuffer,&dwSize,NULL)) { if (GetLastError()==ERROR_HTTP_HEADER_NOT_FOUND) { // Code to handle the case where the header isn't available. return TRUE; } else { // Check for an insufficient buffer. if (GetLastError()==ERROR_INSUFFICIENT_BUFFER) { // Allocate the necessary buffer. lpOutBuffer = new char[dwSize]; // Retry the call. goto retry; } else { // Error handling code. delete [] lpOutBuffer; return FALSE; } } } delete [] lpOutBuffer; return TRUE; } 

On the head begin to move hair. (By the way, delete [] can also be called for a null pointer).

Tricky way


The request has quite a few different headers, but almost all of them are either a string or a number. Ideally, I want to get the header value in one line of code:
 const int code = get_header<HTTP_QUERY_STATUS_CODE>(hRequest) ; const int responseBodySize = get_header<HTTP_QUERY_CONTENT_LENGTH>(hRequest) ; const CString allHeaders = get_header<HTTP_QUERY_RAW_HEADERS_CRLF>(hRequest) ; 

For this, the return type of get_header should depend on the specified header.
 template <DWORD I> TypeDependentOfI get_header(HINTERNET aRequest) { return Magic(aRequest, I) ; } 

And here comes the idiom "traits". Usually it is implemented as a template with specializations, in which typedefs and constants are listed, depending on the template parameters.
 using ATL::CString ; template <DWORD I> struct header_traits ; template <> struct header_traits<HTTP_QUERY_STATUS_CODE> { typedef DWORD result_type ; } ; template <> struct header_traits<HTTP_QUERY_CONTENT_LENGTH> { typedef DWORD result_type ; } ; template <> struct header_traits<HTTP_QUERY_RAW_HEADERS_CRLF> { typedef CString result_type ; } ; 

Of course, I don’t want to write N specializations for all headers. In addition, not only the type of the return value, but also the method of extracting this value will differ. We have to do more cunning. First, we put the mining methods into separate structures (so that these methods can be referenced from traits).
 struct string_getter { typedef CString result_type ; static type get(HINTERNET aRequest, DWORD aValueCode) { DWORD len = 0 ; const BOOL r1 = ::HttpQueryInfo(aRequest, aValueCode, 0, IN OUT &len, 0) ; if (len > 0) { CString res ; const DWORD r2 = ::HttpQueryInfo(aRequest, aValueCode, OUT res.GetBufferSetLength(len + 1), IN OUT &len, 0) ; if (r2 != 0) return res ; } return CString() ; } } ; struct num_getter { typedef DWORD result_type ; static type get(HINTERNET aRequest, DWORD aValueCode) { static const int KMaxLen = 16 ; TCHAR buf[KMaxLen + 1] ; ZeroSizeOf(buf) ; DWORD len = KMaxLen ; if (::HttpQueryInfo(aRequest, aValueCode, OUT buf, IN OUT &len, 0)) { return (DWORD)_tcstoul(buf) ; } return 0 ; } } ; 

And in the traits themselves, we already mention these structures, in which there is information about the type of return value and the method for extracting it.
 template <> struct header_traits<HTTP_QUERY_STATUS_CODE> { typedef num_getter getter ; } ; template <> struct header_traits<HTTP_QUERY_CONTENT_LENGTH> { typedef num_getter getter ; } ; template <> struct header_traits<HTTP_QUERY_RAW_HEADERS_CRLF> { typedef string_getter getter ; } ; 

Then we write convenient macros for listing heading declarations:
 #define DECLARE_STRING_HEADER_GETTER(code) \ template <> struct header_traits<code> \ { typedef string_getter getter ; } ; #define DECLARE_NUM_HEADER_GETTER(code) \ template <> struct header_traits<code> \ { typedef num_getter getter ; } ; DECLARE_NUM_HEADER_GETTER(HTTP_QUERY_STATUS_CODE) DECLARE_NUM_HEADER_GETTER(HTTP_QUERY_CONTENT_LENGTH) DECLARE_STRING_HEADER_GETTER(HTTP_QUERY_RAW_HEADERS_CRLF) DECLARE_STRING_HEADER_GETTER(HTTP_QUERY_CONTENT_DISPOSITION) // ... #undef DECLARE_NUM_HEADER_GETTER #undef DECLARE_STRING_HEADER_GETTER 

In this form, it is much easier and clearer to list the specializations for the necessary headings, especially if they are not 3-4, but several dozen.
As a result, the "magic" method get_header() takes the form:
 template <DWORD I> typename header_traits<I>::getter::type get_header(HINTERNET aRequest) { return typename header_traits<I>::getter::get(aRequest, I) ; } 


Conclusion


The above solution claims to be a bicycle, but so far nothing like it has come across to me on the Internet. In general, the Win32 API is a very fertile ground for such small improvements.
Personally, I am deeply pleased to write such trifles, "syntactic sugar", which make life easier and increase the readability of the code.

')

Source: https://habr.com/ru/post/89922/


All Articles