Как избежать использования глобальной переменной при использовании nftw
Я хочу использовать nftw для обхода структуры каталогов в C.
Однако, учитывая то, что я хочу сделать, я не вижу пути использования глобальной переменной.
Все примеры использования (n) ftw в учебниках включают в себя что-то вроде распечатки имени файла. Вместо этого я хочу взять путь и контрольную сумму файла и поместить их в структуру данных. Но я не вижу хорошего способа сделать это, учитывая ограничения на то, что можно передать в nftw.
Решение, которое я использую, включает в себя глобальную переменную. Затем функция, вызываемая nftw, может получить доступ к этой переменной и добавить необходимые данные.
Есть ли разумный способ сделать это без использования глобальной переменной?
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);
}