Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 209
Jason Kleban Avatar asked Mar 12 '14 20:03

Jason Kleban


People also ask

Does std::unique_ptr follow the RAII design principle?

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

What is the use of RAII in Java?

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.

Is it possible to use a smart pointer for RAII?

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!

What is RAII (Resource acquisition is initialization)?

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.


1 Answers

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))
// ...
like image 60
Mark Ransom Avatar answered Sep 17 '22 20:09

Mark Ransom