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!
Yes, std::unique_ptr follows the RAII design principle. No, std::unique_ptr does not prevent other code from doing something stupid, like calling delete on a pointer that belongs to the unique_ptr. The unique_ptr itself will call a deleter 1 on the object it owns when either: it goes out of scope
RAII guarantees that the resource is available to any function that may access the object (resource availability is a class invariant, eliminating redundant runtime tests). It also guarantees that all resources are released when the lifetime of their controlling object ends, in reverse order of acquisition.
std::unique_ptr can be used for RAII, but doesn't prevent you from doing something like this: Usually, the best way to use smart pointers is to pass the raw pointer as their constructor or make-like function, such as Thanks for contributing an answer to Stack Overflow!
Resource Acquisition Is Initialization or RAII, is a C++ programming technique [1] [2] which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, database connection—anything that exists in limited supply) to the lifetime of an object.
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))
// ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With