Search code examples
c++winapidynamic-memory-allocationout-parameters

RAII, unique_ptr, and out parameters


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

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

But I learned that there is no finally block and that windns.h really should get with the times and implement an-RAII compliant interface (as I understand the typical advice). Instead of waiting for that to happen, I tried to make an RAII wrapper class whose destructor calls DnsRecordListFree() and with a operator overload cast to get the original pointer.

But I was confused about how to appropriately use this handle or the pointer to get an out parameter. And while I researched that I learned how unique_ptr, which I'd already learned a little bit about, can be used with a custom deleter.

So here's my simple code so far. There's probably more wrong than just this, but I figure I could declare yet another PDNS_RECORD *presult and use that as the out parameter and then copy or move or otherwise assign its value into a unique_ptr, but that sounds like too much work/mess.

It seems to me that unique_ptr's internal pointer should be initialized to NULL, that I should somehow be able to pass the pointer's address into the out parameter, that DNSQuery will update the raw value, and when the unique_ptr falls out of scope in my function the DnsRecordListFree() call will be made automatically. There's just too much I don't know to figure out the right combination for the minimal correct/safe usage.

#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!


Solution

  • You could spend all day trying to adapt the standard smart pointer, or you could just write your own. They're not hard to do, especially if you're willing to cheat 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))
    // ...