2012-04-23 17 views
7

quiero usar nftw para atravesar una estructura de directorios en C.Cómo evitar el uso de variables globales cuando se utiliza nftw

Sin embargo, teniendo en cuenta lo que quiero hacer, no veo una forma de evitar el uso de una variable global.

Los ejemplos de libros de texto de using (n) ftw implican hacer algo así como imprimir un nombre de archivo. En cambio, quiero tomar la ruta de acceso y la suma de comprobación del archivo y ubicarlos en una estructura de datos. Pero no veo una buena manera de hacerlo, dados los límites de lo que se puede pasar a nftw.

La solución que estoy utilizando implica una variable global. La función llamada por nftw puede acceder a esa variable y agregar los datos requeridos.

¿Hay alguna forma razonable de hacerlo sin usar una variable global?

Here's the exchange in previous post on stackoverflow in which someone suggested I post this as a follow-up.

Respuesta

2

nftw no ofrecer cualquier parámetro de usuario que podrían ser pasados ​​a la función, así que hay que utilizar variables globales (o estática) en C.

GCC ofrece una extensión " función anidada", que debe capturar las variables de sus ámbitos de aplicación de cerramiento, por lo que se podrían utilizar como esto:

void f() 
{ 
    int i = 0; 
    int fn(const char *, 
    const struct stat *, int, struct FTW *) { 
    i++; 
    return 0; 
    }; 
    nftw("path", fn, 10, 0); 
} 
1

es mejor dada la vinculación de datos estática (es decir, archivo-scope) en un módulo separado que incluye sólo f se requieren las funciones para acceder a los datos, incluida la función que se pasó al nftw(). De esta forma, los datos no son visibles globalmente y todos los accesos están controlados. Puede ser que la función que llama a ntfw() también sea parte de este módulo, permitiendo que la función pasada a nftw() también sea estática y, por lo tanto, invisible desde el exterior.

En otras palabras, debe hacer lo que probablemente ya está haciendo, pero use la compilación separada y la vinculación estática para hacer que los datos solo sean visibles a través de las funciones de acceso. Los datos con enlace estático son accesibles por cualquier función dentro de la misma unidad de traducción, y usted evita los problemas asociados con las variables globales al incluir solo las funciones en esa unidad de traducción que son creadores, mantenedores o usuarios de esos datos.

El patrón general es:

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 
} 
+0

Conocía los méritos de hacer gran parte del aislamiento del código, ya que la biblioteca BSD "libarchive" (que también estoy usando) está configurada de esa manera. Pero su comentario ayudó a reforzar el problema, y ​​utilizaré la palabra clave estática de manera más prolífica como resultado. (Lo recomendaría si pudiera). – user1071847

+1

El artículo [Pox on globals] (http://www.eetimes.com/discussion/break-point/4025723/A-pox-on-globals) lo he vinculado en su publicación anterior también discute esta técnica. Que el artículo se refiere a sistemas integrados, pero no es menos aplicable a la informática de propósito general. También sugeriría que C++ le brinde más oportunidades para una mejor encapsulación de datos mediante el uso de clases y espacios de nombres. Algunas precauciones sobre el uso de estática de esta manera, está bien si solo necesita una instancia de los datos, y resulta en código no reentrante y no seguro para subprocesos. Se pueden aplicar otras soluciones. – Clifford

1

Usando ftw puede ser muy, muy mala . Internamente, guardará el puntero de función que use, si otro hilo hace otra cosa sobrescribirá el puntero de la función.

escenario del horror:

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. 

En resumen. Es mejor que uses fts_open.

Si aún desea usar nftw, entonces mi sugerencia es colocar el tipo "global" en un espacio de nombres y marcarlo como "thread_local".Debería poder ajustar esto a sus necesidades.

/* 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; 
} 
+0

La implementación de glibc de ftw guarda el puntero de la función en la pila, no en una variable global, por lo que el escenario de horror que enumeró no es posible. Dicho esto, creo que su sugerencia de usar thread_local es la mejor respuesta a esta pregunta. –

+0

... pero fts está menos extendido que ftw. – fche

Cuestiones relacionadas