Question

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!

Was it helpful?

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))
// ...

OTHER TIPS

Unless I'm missing something (I haven't a Windows box handy to compile this, so forgive me if I am) your code is incorrect. Personally i wouldn't do this with a smart pointer, since all you're essentially using it for is a custom janitor class (and you could write one of those easy-enough).

Regardless, first, your deleter 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 your loadup of this smart pointer should be after determination the function succeeded:

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

If I got that right, your deleter will be fired correctly on scope exit on the fetched chain. But again, this seems an awful lot of work for what you can effectively make simpler with your own janitor class.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top