shared_ptr и файл для обертывания с cstdio (обновление: также dlfcn.з)


Даже в присутствии там может быть причин для использования файл интерфейса. Мне было интересно, если оборачивание файл* в shared_ptr будет полезным строительства, или если он имеет какие-либо опасные подводные камни:

#include <cstdio>
#include <memory>

std::shared_ptr<std::FILE> make_file(const char * filename, const char * flags)
{
  std::FILE * const fp = std::fopen(filename, flags);
  return fp ? std::shared_ptr<std::FILE>(fp, std::fclose) : std::shared_ptr<std::FILE>();
}

int main()
{
  auto fp = make_file("hello.txt", "wb");
  fprintf(fp.get(), "Hello world.");
}

Обновление: я просто понял, что это не позволено функции fclose нулевой указатель. Я изменил make_file таким образом, чтобы в случае провала не будет особых делетер.


Второе обновление: я также понял, что unique_ptr не может быть более подходящим, чем shared_ptr. Вот более общий подход:

typedef std::unique_ptr<std::FILE, int (*)(std::FILE *)> unique_file_ptr;
typedef std::shared_ptr<std::FILE> shared_file_ptr;

static shared_file_ptr make_shared_file(const char * filename, const char * flags)
{
  std::FILE * const fp = std::fopen(filename, flags);
  return fp ? shared_file_ptr(fp, std::fclose) : shared_file_ptr();
}

static unique_file_ptr make_file(const char * filename, const char * flags)
{
  return unique_file_ptr(std::fopen(filename, flags), std::fclose);
}

Редактировать. В отличие от shared_ptr, unique_ptr не только вызывает делетер, если указатель не равен нулю, поэтому мы можем упростить осуществление make_file.

Третье обновление: можно построить общий указатель с уникальным указателем:

unique_file_ptr up = make_file("thefile.txt", "r");
shared_file_ptr fp(up ? std::move(up) : nullptr);  // don't forget to check

Четвертый обновление: подобная конструкция может использоваться для функции выглядит()/dlclose():

#include <dlfcn.h>
#include <memory>

typedef std::unique_ptr<void,  int (*)(void *)> unique_library_ptr;

static unique_library_ptr make_library(const char * filename, int flags)
{
  return unique_library_ptr(dlopen(filename, flags), dlclose);
}


13477
43
задан 8 сентября 2011 в 01:09 Источник Поделиться
Комментарии
2 ответа

Честно говоря, я думал очень трудно придумать какое-нибудь реальный недостаток это, возможно, но я не могу придумать ничего. Это, конечно, выглядит странно, чтобы обернуть структуры C в shared_ptr, но пользовательским deleter заботится о том, что проблема, так это просто субъективная неприязнь, и только на первый взгляд. На самом деле сейчас, мне кажется, это довольно умно.

21
ответ дан 11 октября 2011 в 03:10 Источник Поделиться

Я должен начать с того, что я полностью не согласен с широко распространенным убеждением, что "явное лучше, чем неявное". Я думаю, в данном случае, это, наверное, как минимум не хуже, чтобы иметь класс, который просто неявно преобразует в нужный вид:

class file { 
typedef FILE *ptr;

ptr wrapped_file;
public:
file(std::string const &name, std::string const &mode = std::string("r")) :
wrapped_file(fopen(name.c_str(), mode.c_str()))
{ }

operator ptr() const { return wrapped_file; }

~file() { if (wrapped_file) fclose(wrapped_file); }
};

Я не пытался сделать это подвижные, но та же общая идея будет применяться, если вы сделали. Это (среди прочего) то преимущество, что вы работаете с файлом напрямую в файл, а не уродливое (и бесполезных) .получить() бородавка, так что код будет что-то вроде:

file f("myfile.txt", "w");

if (!f) {
fprintf(stderr, "Unable to open file\n");
return 0;
}

fprintf(f, "Hello world");

Это имеет несколько преимуществ. Вышеупомянутый чистота является очень важным. Другой факт, что пользователь имеет достаточно нормального типа объекта, поэтому, если они хотят использовать перегрузку примерно так, как они бы с собой поток, что довольно легко, как хорошо:

file &operator<<(file &f, my_type const &data) { 
return data.write(f);
}

// ...

file f("whatever", "w");
f << someObject;

Короче говоря, если пользователь хочет сделать c-стиля ввода-вывода, который работает нормально. Если он предпочитает делать то, что хочется, как использование iostreams с, большое, что это довольно легко, а также поддержку. Большинству это просто синтаксический сахар, хотя, так что это вообще не накладывает никаких издержек по сравнению с использованием файл * напрямую.

25
ответ дан 17 ноября 2011 в 03:11 Источник Поделиться