2011-08-31 16 views
7
FUNC(param); 

Se param è char *, inviare a func_string.Come implementare una macro generica in C?

quando è int, la spedizione verso func_int

Penso che ci può essere una soluzione a questo, come tipi di variabili sono noti al momento della compilazione ..

+0

Anche se fosse possibile, sarebbe fugace. C non è stato progettato per questo. Usa le strutture e i sindacati, puoi persino ottenere la spedizione in tempo reale in questo modo. –

risposta

10

Tipi di variabili sono noti per il compilatore, ma non per il preprocessore (che vede il codice semplicemente come di testo non strutturato un flusso di token e esegue solo semplici operazioni di sostituzione su di esso). Quindi temo che non puoi raggiungere questo obiettivo con i macro C.

In C++, hanno inventato modelli per risolvere tali problemi (e altro ancora).

+0

C'è qualche problema con ** C **? – lexer

+2

Questa è una falsa conclusione. Il preprocessore non ha bisogno di conoscere i tipi di argomenti, se è possibile organizzare un'espansione macro in modo che il * compilatore * possa eseguire l'analisi. Vedi la risposta di arnaud576875 su come risolvere questo problema con il compilatore gnu. Inoltre, il preprocessore non vede il codice come testo non strutturato, ma come un * token stream *, che è una differenza importante. –

1

Non è possibile farlo con una macro. Il valore della macro viene sostituito al momento della compilazione e non viene interpretato. Sono solo sostituzioni.

11

Questo sarà possibile con C1X ma non nello standard corrente.

Sarà simile a questa:

#define cbrt(X) _Generic((X), long double: cbrtl, \ 
          default: cbrt, \ 
          float: cbrtf)(X) 
+0

Come questa domanda appare prima su google: questo è ora possibile utilizzando C11 http://en.cppreference.com/w/c/language/generic –

4

Non c'è alcuna possibilità di eseguire i tipi di controllo orario di C89/ANSI C, ma v'è un'estensione a GCC che le permette. typeof o qualcosa del genere se ricordo. L'ho visto nel kernel Linux una volta.

In kernel.h:

#define min(x, y) ({    \ 
typeof(x) _min1 = (x);   \ 
typeof(y) _min2 = (y);   \ 
(void) (&_min1 == &_min2);  \ 
_min1 < _min2 ? _min1 : _min2; }) 

Date un'occhiata a questo articolo: GCC hacks in the Linux kernel

Quando ho visto la prima volta questo in realtà ho fatto una domanda qui su SO su:

min macro in kernel.h

Non sono proprio sicuro di come lo useresti per risolvere il tuo problema, ma è qualcosa che vale la pena dare un'occhiata a t.

+0

questo non è un tipo di controllo di runtime - come C non è digitato in modo dinamico, tipo le informazioni sono disponibili solo al momento della compilazione – Christoph

1

I tipi di variabile sono effettivamente noti al momento della compilazione, tuttavia l'espansione delle macro avviene prima della compilazione. Ti suggerisco di implementare 2 funzioni sovraccaricate invece di una macro.

5

È possibile verificare le caratteristiche dei tipi.

Ad esempio, int può contenere un valore negativo, mentre char* non può. Quindi, se ((typeof(param))-1) < 0, param non è firmato:

if (((typeof(param))-1) < 0) { 
    do_something_with_int(); 
} else { 
    do_something_with_char_p(); 
} 

Il compilatore ottimizza ovviamente questo fuori.

Prova qui: http://ideone.com/et0v1

Questo sarebbe ancora più facile se i tipi hanno diverse dimensioni.Per esempio, se si vuole scrivere una macro generica che può gestire diverse dimensioni dei caratteri:

if (sizeof(param) == sizeof(char)) { 
    /* ... */ 
} else if (sizeof(param) == sizeof(char16_t)) { 
    /* ... */ 
} else if (sizeof(param) == sizeof(char32_t)) { 
    /* ... */ 
} else { 
    assert("incompatible type" && 0); 
} 

GCC ha una __builtin_types_compatible_p() funzione built-in che possono verificare la compatibilità tipi:

if (__builtin_types_compatible_p(typeof(param), int)) { 
    func_int(param); 
} else if (__builtin_types_compatible_p(typeof(param), char*)) { 
    func_string(param); 
} 

Provalo qui: http://ideone.com/lEmYE

È possibile inserire questo in una macro per ottenere ciò che si sta tentando di fare:

#define FUNC(param) ({            \ 
    if (__builtin_types_compatible_p(typeof(param), int)) {   \ 
     func_int(param);            \ 
    } else if (__builtin_types_compatible_p(typeof(param), char*)) { \ 
     func_string(param);           \ 
    }                 \ 
}) 

(Lo ({...}) è un GCC's statement expression, consente a un gruppo di istruzioni di essere un valore.

Il builtin __builtin_choose_expr() può scegliere l'espressione da compilare. Con __builtin_types_compatible_p questo permette di attivare un errore al momento della compilazione se il tipo di parametro non è compatibile con entrambi int e char*: (compilando somehting valido in questo caso)

#define FUNC(param)              \ 
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), int) \ 
     , func_int(param)             \ 
     , __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), char*) \ 
      , func_string(param)           \ 
      , /* The void expression results in a compile-time error  \ 
       when assigning the result to something. */    \ 
      ((void)0)              \ 
     )                 \ 
    ) 

Questo è in realtà un esempio leggermente modificato da __builtin_choose_expr docs.

+0

'func_int (x);', 'func_string (x);' e inserisce l'intera cosa all'interno di un 'do {} while (0)' –

+0

Originariamente non ho incluso il codice in 'do {} while()' perché quella non era una macro ;-) – arnaud576875

+2

-1 'typeof' non è C ma anche un'estensione gcc. Cosa ti fa pensare che un indirizzo non possa avere il bit 'segno' impostato? –

Problemi correlati