Как избежать использования глобальной переменной при использовании nftw

Я хочу использовать nftw для обхода структуры каталогов в C.

Однако, учитывая то, что я хочу сделать, я не вижу пути использования глобальной переменной.

Все примеры использования (n) ftw в учебниках включают в себя что-то вроде распечатки имени файла. Вместо этого я хочу взять путь и контрольную сумму файла и поместить их в структуру данных. Но я не вижу хорошего способа сделать это, учитывая ограничения на то, что можно передать в nftw.

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

Есть ли разумный способ сделать это без использования глобальной переменной?

Вот обмен в предыдущем посте на stackru, в котором кто-то предложил мне опубликовать это как продолжение.

3 ответа

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

Ужасный сценарий:

thread 1:  count billions of files
thread 2:  delete some files
thread 1:  ---oops, it is now deleting billions of 
              files instead of counting them.

Короче. Вам лучше использовать fts_open.

Если вы все еще хотите использовать nftw, тогда я предлагаю поместить "глобальный" тип в пространство имен и пометить его как "thread_local". Вы должны быть в состоянии приспособить это к вашим потребностям.

/* in some cpp file */
namespace {
   thread_local size_t gTotalBytes{0};  // thread local makes this thread safe
int GetSize(const char* path, const struct stat* statPtr, int currentFlag, struct FTW* internalFtwUsage) {
    gTotalBytes+=  statPtr->st_size;
    return 0;  //ntfw continues
 }
} // namespace


size_t RecursiveFolderDiskUsed(const std::string& startPath) {
   const int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS;
   const int maxFileDescriptorsToUse = 1024; // or whatever
   const int result = nftw(startPath.c_str(), GetSize, maxFileDescriptorsToUse , flags);

  // log or something if result== -1
  return gTotalBytes;
}

Данные лучше всего получить в виде статической связи (т. Е. Области видимости файла) в отдельном модуле, который включает только функции, необходимые для доступа к данным, включая функцию, переданную в nftw(), Таким образом, данные не видны глобально, и весь доступ контролируется. Может случиться так, что функция, которая вызывает ntfw(), также является частью этого модуля, что позволяет функции, передаваемой в nftw(), быть также статической и, следовательно, невидимой извне.

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

Общий шаблон:

datamodule.h

#if defined DATAMODULE_INCLUDE
<type> create_data( <args>) ;
<type> get_data( <args> ) ;
#endif

datamodule.c

#include "datamodule.h"

static <type> my_data ;

static int nftwfunc(const char *filename, const struct stat *statptr, int fileflags, struct FTW *pfwt)
{
    // update/add to my_data
    ...
}


<type> create_data( const char* path, <other args>)
{
    ...

    ret = nftw( path, nftwfunc, fd_limit, flags);

    ... 
}

<type> get_data( <args> )
{
    // Get requested data from my_data and return it to caller
}

Нет. nftw не предлагает никаких пользовательских параметров, которые могли бы быть переданы в функцию, поэтому вы должны использовать глобальные (или статические) переменные в C.

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

void f()
{
  int i = 0;
  int fn(const char *,
    const struct stat *, int, struct FTW *) {
    i++;
    return 0;
  };
  nftw("path", fn, 10, 0);
}
Другие вопросы по тегам