RAII, unique_ptr, and out parameters

I am a C # developer trying to learn C ++ 11. I am trying to query DNS using windns.h.

I started with DnsQuery() and read that I need to free the parameter for writing results using DnsRecordListFree() . The C # way might be to use a try-finally block to ensure that I release the resource no matter what.

But I found out that there is no finally block, and that windns.h really needs to get the time and implement an interface compatible with RAII (as I understand a typical tip). Instead of waiting for this to happen, I tried to create an RAII wrapper class whose destructor calls DnsRecordListFree() , and with the operator overload the cast to get the original pointer.

But I was confused about how to properly use this handle or pointer to get the out parameter. And while I was researching what I learned, how unique_ptr , which I already learned a little about, can be used with user deletion.

So here is my simple code. This is most likely wrong, but I suppose I can declare another PDNS_RECORD *presult and use this as an out parameter and then copy or move or otherwise assign its value to unique_ptr , but that sounds like too much work / mess.

It seems to me that the unique_ptr internal pointer should be initialized to NULL , that I must somehow pass the address of the pointer to the out parameter so that DNSQuery original value, and when unique_ptr goes beyond the scope of my function, the call to DnsRecordListFree() will be executed automatically. There are too many, I do not know to determine the right combination for the minimum correct / safe use.

 #include <iostream> #include <fstream> #include <memory> #include <Windows.h> #include <WinDNS.h> using namespace std; auto pdnsDeleter = [&](PDNS_RECORD *ptr){ if (ptr) DnsRecordListFree(ptr); }; int main(int argc, char **argv) { cout << "Hello World\n"; std::unique_ptr<PDNS_RECORD*, decltype(pdnsDeleter)> results(0, pdnsDeleter); if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, ??results??, NULL)) { cout << "google.com -> " << ??results??; } cout << "Done\n"; getchar(); return 0; } 

Thanks!

+7
c ++ winapi dynamic-memory-allocation
source share
2 answers

You could spend all day trying to adapt a standard smart pointer, or just write your own. They are easy to make, especially if you want to trick and allow access to the raw pointer itself.

 struct DnsRAII { PDNS_RECORD p; DnsRAII() : p(NULL) { } ~DnsRAII() { if (p != NULL) DnsRecordListFree(p, DnsFreeRecordList); } }; DnsRAII results; if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &results.p, NULL)) // ... 
+5
source share

If something is missing (I don’t have a windows window to compile it, so forgive me if I am), your code is incorrect. Personally, I would not do this with a smart pointer, since all you mostly use for it is the janitor custom class (and you could write one of them quite simply).

Regardless, firstly, your flow rate should be:

 auto pdnsDeleter = [&](PDNS_RECORD ptr){ if (ptr) DnsRecordListFree(ptr, DnsFreeRecordList); }; 

Next, the type of your smart pointer should be:

 std::unique_ptr<DNS_RECORD, decltype(pdnsDeleter)> results; 

Finally, I believe that your loading of this smart pointer should be successful after defining the function:

 PDNS_RECORD pdnsrec; if (DnsQuery(L"google.com", DNS_TYPE_A, DNS_QUERY_STANDARD, NULL, &pdnsrec, NULL)) { results.reset(pdnsrec); } 

If I got this right, your loser will be correctly launched at the output of the area on the selected chain. But then again, this seems like a very complicated job for what you can make easier with your own janitor class.

+2
source share

All Articles