2016-01-03 8 views
13

Penso che l'estensione GCC __attribute__((cleanup)) sia una buona idea, almeno per alcuni casi, ma non riesco a capire come usarlo in un buon modo. Tutto quello che sto facendo sembra ancora molto fastidioso.Un modo valido e idiomatico per usare GCC e clang __attribute __ ((cleanup) e dichiarazioni puntatore

ho visto un sacco di codice di fare #define _cleanup_(x) __attribute__((cleanup(x)) solo per digitare meno, ma c'è un modo per passare lì una funzione standard come free o closedir, fclose, ecc?

Per come la vedo io non posso solo scrivere:

__attribute__((cleanup(free))) char *foo = malloc(10); 

Perché il callback pulizia riceverà char** puntatore, e ho dovuto scrivere sempre qualcosa di simile:

static void free_char(char **ptr) { free(*ptr); } 
__cleanup__((free_char)) char *foo = malloc(10); 

Questo è abbastanza fastidioso, e la parte più fastidiosa è definire tali funzioni di pulizia per tutti i tipi di cui hai bisogno, perché ovviamente non puoi semplicemente definirlo per void **. Qual è il modo migliore per evitare queste cose?

+6

Penso che tu abbia fatto un buon esempio per dire che "__attribute __ ((cleanup))" è ** non ** una buona idea. – user3386109

+7

Se vuoi i distruttori sai dove trovarli. –

+0

@ user3386109, direi che è una buona idea, ma la realizzazione è un po 'rotta. O forse c'è qualcosa che non capisco, ecco perché lo sto chiedendo. – coredump

risposta

8

C'è una biblioteca che costruisce general-purpose puntatori intelligenti (unique_ptr e shared_ptr) sulla parte superiore del __attribute__((cleanup)) qui: https://github.com/Snaipe/libcsptr

Esso consente di scrivere il codice di livello superiore in questo modo:

#include <stdio.h> 
#include <csptr/smart_ptr.h> 
#include <csptr/array.h> 

void print_int(void *ptr, void *meta) { 
    (void) meta; 
    // ptr points to the current element 
    // meta points to the array metadata (global to the array), if any. 
    printf("%d\n", *(int*) ptr); 
} 

int main(void) { 
    // Destructors for array types are run on every element of the 
    // array before destruction. 
    smart int *ints = unique_ptr(int[5], {5, 4, 3, 2, 1}, print_int); 
    // ints == {5, 4, 3, 2, 1} 

    // Smart arrays are length-aware 
    for (size_t i = 0; i < array_length(ints); ++i) { 
     ints[i] = i + 1; 
    } 
    // ints == {1, 2, 3, 4, 5} 

    return 0; 
} 

Per quanto riguarda l'idiomatico, però? Bene, quanto sopra è certamente vicino al linguaggio C++ idiomatico. Non così tanto. La funzione è chiaramente supportata principalmente in GCC e Clang perché hanno anche compilatori C++, quindi hanno la possibilità di utilizzare i macchinari RAII nel frontend C senza alcun costo aggiuntivo; non è una buona idea scrivere in questo modo C-destinato-come-C. Si basa in genere su un compilatore C++ presente nonostante non sia effettivamente utilizzato.

Se fossi in me, probabilmente indagherei sull'implementazione di pool di autorelease o qualcosa di simile che possa essere effettivamente eseguito in puro C a livello di lingua. Dipende da quanto velocemente hai bisogno di liberare le tue risorse; per la memoria, di solito puoi vivere senza una pulizia immediata.

5

Non è possibile scrivere __attribute__((cleanup(free))), ma non è necessario scrivere una funzione di pulizia free per ogni tipo. E 'brutto, ma è possibile scrivere questo:

static void cleanup_free(void *p) { 
    free(*(void**) p); 
} 

ho visto questo in the systemd codebase.

Per altre funzioni, in generale, è necessario scrivere un wrapper con un livello aggiuntivo di riferimento indiretto per l'uso con __attribute__((cleanup)). systemddefines a helper macro for this:

#define DEFINE_TRIVIAL_CLEANUP_FUNC(type, func)    \ 
    static inline void func##p(type *p) {     \ 
      if (*p)           \ 
        func(*p);        \ 
    }              \ 
    struct __useless_struct_to_allow_trailing_semicolon__ 

utilizzata all over the place, ad esempio

DEFINE_TRIVIAL_CLEANUP_FUNC(FILE*, pclose); 

#define _cleanup_pclose_ __attribute__((cleanup(pclosep))) 
Problemi correlati