Параметры RAII, unique_ptr и out

Я разработчик C #, пытающийся изучать C ++ 11. Я пытаюсь запросить DNS с помощью windns.h.

Я начал с DnsQuery() и читать, что мне нужно освободить параметр записи результатов с DnsRecordListFree(), C # способ может быть использовать try-finally заблокировать, чтобы я освободил ресурс, несмотря ни на что.

Но я узнал, что нет finally block и тот windns.h действительно должен идти в ногу со временем и реализовывать интерфейс, совместимый с RAII (как я понимаю, типичный совет). Вместо того чтобы ждать, пока это произойдет, я попытался создать класс-оболочку RAII, чей деструктор вызывает DnsRecordListFree() и с приведением перегрузки оператора, чтобы получить исходный указатель.

Но я был озадачен тем, как правильно использовать этот дескриптор или указатель для получения параметра out. И пока я исследовал, что я узнал, как unique_ptr, о котором я уже немного узнал, можно использовать с пользовательским средством удаления.

Итак, вот мой простой код. Там, вероятно, больше, чем просто это неправильно, но я полагаю, я мог бы объявить еще один PDNS_RECORD *presult и использовать его в качестве параметра out, а затем скопировать или переместить или иным образом назначить его значение в unique_ptr, но это звучит как слишком много работы / беспорядка.

Мне кажется, что unique_ptrвнутренний указатель должен быть инициализирован NULLчто я должен каким-то образом быть в состоянии передать адрес указателя в выходной параметр, что DNSQuery обновит необработанное значение, и когда unique_ptr выходит за рамки моей функции DnsRecordListFree() звонок будет сделан автоматически. Слишком много, я не знаю, чтобы найти правильную комбинацию для минимального правильного / безопасного использования.

#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;
}

Спасибо!

7

Решение

Вы можете потратить весь день на адаптацию стандартного интеллектуального указателя или написать свой собственный. Их не сложно сделать, особенно если вы хотите обмануть и разрешить доступ к самому необработанному указателю.

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

Другие решения

Если я что-то упустил (у меня нет окна Windows, удобного для компиляции, так что простите, если я), ваш код неверен. Лично я не стал бы делать это с помощью умного указателя, поскольку все, для чего вы по сути его используете, — это собственный класс уборщика (и вы могли бы написать один из этих достаточно простых).

Независимо от того, во-первых, ваш удалитель должен быть:

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

Далее тип вашего умного указателя должно быть:

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

Наконец, я верю вашему LoadUp этого умного указателя должно быть после определения, что функция выполнена успешно:

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

Если я правильно понял, ваш удалитель будет корректно сработать при выходе из области в выбранной цепочке. Но опять же, это кажется огромной работой для того, что вы можете эффективно упростить с вашим собственным классом уборщика.

2

По вопросам рекламы [email protected]