2013-03-05 15 views
14

Spesso mi ritrovo a scrivere metodi helper debugger che restituiscono una stringa stampabile, dato un certo valore enum. La ragione di ciò è quando di solito registri un enum, tutto quello che ottieni è davvero un numero. Odio dover tornare alla mia fonte per capire cosa sia quell'enum. Quindi mi sento di fare qualcosa di simileEsistono trucchi del compilatore/preprocessore per eseguire il debug di stampa del nome di un enum?

typedef enum 
{ 
    kOne = 1, 
    kTwo, 
    kThree, 
} 
MyEnum; 

NSString *debugStringForEnum(MyEnum e) 
{ 
    switch (e) 
     case kOne: 
      return @"One"; 
     case kTwo: 
      return @"Two"; 
     .... 
} 

.... 
NSLog(@"My debug log: %@", debugStringForEnum(someVariable)); 

Quindi la mia domanda è: esiste un modo per evitare di scrivere tutto il codice di supporto, solo per vedere il valore dell'etichetta di enum?

Grazie

+0

Per quanto ne so non c'è un modo per farlo, a meno di scrivere il tuo metodo, come hai fatto tu. –

+0

Sfortunatamente non è possibile poiché i nomi enum non sono disponibili in una compilazione passata. – iDev

+0

possibile duplicato di [Converti obiettivo-c typedef nella sua stringa equivalente] (http://stackoverflow.com/questions/1094984/convert-objective-c-typedef-to-its-string-equivalent). Puoi utilizzare uno dei metodi suggeriti lì. – iDev

risposta

22

Se sei disposto a scrivere codice "birichino" che fa piangere altri sviluppatori, allora sì. Prova questo:

#define ENUM(name, ...) typedef enum { M_FOR_EACH(ENUM_IDENTITY, __VA_ARGS__) } name; \ 
char * name ## _DEBUGSTRINGS [] = { M_FOR_EACH(ENUM_STRINGIZE, __VA_ARGS__) }; 

#define ENUM_IDENTITY(A) A, 
#define ENUM_STRINGIZE(A) #A, 

ENUM(MyEnum, 
     foo, bar, baz, boo 
     ) 

Ovviamente hai bisogno di una macro for-each per farlo funzionare. Ecco un semplice:

#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N 

#define M_CONC(A, B) M_CONC_(A, B) 
#define M_CONC_(A, B) A##B 

#define M_FOR_EACH(ACTN, ...) M_CONC(M_FOR_EACH_, M_NARGS(__VA_ARGS__)) (ACTN, __VA_ARGS__) 

#define M_FOR_EACH_0(ACTN, E) E 
#define M_FOR_EACH_1(ACTN, E) ACTN(E) 
#define M_FOR_EACH_2(ACTN, E, ...) ACTN(E) M_FOR_EACH_1(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_3(ACTN, E, ...) ACTN(E) M_FOR_EACH_2(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_4(ACTN, E, ...) ACTN(E) M_FOR_EACH_3(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_5(ACTN, E, ...) ACTN(E) M_FOR_EACH_4(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_6(ACTN, E, ...) ACTN(E) M_FOR_EACH_5(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_7(ACTN, E, ...) ACTN(E) M_FOR_EACH_6(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_8(ACTN, E, ...) ACTN(E) M_FOR_EACH_7(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_9(ACTN, E, ...) ACTN(E) M_FOR_EACH_8(ACTN, __VA_ARGS__) 
#define M_FOR_EACH_10(ACTN, E, ...) ACTN(E) M_FOR_EACH_9(ACTN, __VA_ARGS__) 

Dovrebbe essere ovvio come estendere quel loop di avere un limite superiore di più, ma ... le considerazioni di spazio per questa risposta. Il ciclo può essere potenzialmente lungo quanto sei disposto a copiare e incollare più iterazioni in questo bit.

Per la generazione non di debug, avere un #ifdef scegliere una versione di ENUM senza la seconda riga.


EDIT: di rubare l'inizializzatori idea designato dal teppic, ecco una versione ancora più orribile che funziona anche con i non-ordinato initialiser valori:

#define ENUM(name, ...) typedef enum { M_FOR_EACH(ENUM_ENAME, __VA_ARGS__) } name; \ 
char * name ## _DEBUGSTRINGS [] = { M_FOR_EACH(ENUM_ELEM, __VA_ARGS__) }; 

#define ENUM_ENAME(A) M_IF(M_2ITEMS(M_ID A), (M_FIRST A = M_SECOND A), (A)), 
#define ENUM_ELEM(A) M_IF(M_2ITEMS(M_ID A), ([M_FIRST A] = M_STR(M_FIRST A)), ([A] = M_STR(A))), 

#define M_STR(A) M_STR_(A) 
#define M_STR_(A) #A 
#define M_IF(P, T, E) M_CONC(M_IF_, P)(T, E) 
#define M_IF_0(T, E) M_ID E 
#define M_IF_1(T, E) M_ID T 
#define M_2ITEMS(...) M_2I_(__VA_ARGS__, 1, 0) 
#define M_2I_(_2, _1, N, ...) N 
#define M_FIRST(A, ...) A 
#define M_SECOND(A, B, ...) B 
#define M_ID(...) __VA_ARGS__ 

ENUM(MyEnum, 
     foo, bar, baz, boo 
     ) 

ENUM(NotherEnum, 
     A, B, (C, 12), D, (E, 8) 
     ) 

Non posso garantire la vostra sicurezza personale, se usi questo tipo di cose nel codice che qualcun altro deve mantenere.

+2

I macro preprocessuali che abusano ottengono sempre un upvote –

+1

Eww. vinci +1 Internet per quel post però :) –

+0

Ho scritto un esempio su come macrolizzare le parti delle funzioni qui: http://stackoverflow.com/a/14931957/1162141 – technosaurus

7

Un metodo più semplice è quello di costituire una matrice di stringhe che replicano le etichette secondo la loro posizione nella matrice, ad esempio

char *enumlabels[] = { NULL, "KOne", "KTwo", "KThree"}; 

Qui è necessario riempire gli spazi per rendere i valori di enumerazione corrispondenti alle posizioni dell'array.

O, meglio, per la C99 con inizializzatori designati:

char *enumlabels[] = { [1] = "KOne", [2] = "KTwo", [3] = "KThree"}; 

In questo caso, fino a quando la dichiarazione enum viene prima, è possibile scambiare i indici di matrice direttamente per i valori enum per renderlo più chiaro , per esempio { [kOne] = "kOne", ... }.

quindi per MyEnum e è possibile utilizzare solo printf("%s\n", enumlabels[e]); o alcuni di questi.

Ho scritto un po 'di codice per dimostrare questo molto meglio:

typedef enum { 
    white = 1, 
    red = 2, 
    green = 4, 
    blue = 8, 
    black = 16 
} Colour; // be sure to update array below! 

char *enum_colours[] = { [white] = "white", [red] = "red", [green] = "green", 
         [blue] = "blue", [black] = "black" }; 

Colour c = blue; 
printf("The label for %d is %s\n", c, enum_colours[c]); 

Output: The label for 8 is blue

Se si dispone di enormi costanti enum (ad esempio 32767) Questo ovviamente non è una soluzione ideale, a causa di la dimensione della matrice richiesta. Senza inizializzatori designati, è possibile assegnare direttamente i valori dell'array, se più laboriosamente, con enum_colours[white] = "white"; e così via, ma solo in una funzione.

+0

Mi piace questa soluzione tranne che mi preoccupo di cosa succede quando qualcuno aggiunge un enum all'enumerazione ma dimentica di estendere l'array di descrizione ... –

+0

Posizionali in una struttura insieme in modo che entrambi debbano essere aggiornati insieme. –

+1

Questo presuppone che il valore enum inizi a zero, il che potrebbe non essere sempre il caso. Può essere definito per iniziare con qualsiasi numero. – iDev

0

I nomi stessi wont essere a disposizione, ma è di solito sufficiente per dare loro un numero esplicito:

enum{ 
    dog = 100, 
    cat = 200, 
    problem = dog+cat, //trailing comma legal in C and C++11 
}; 
+0

La virgola finale è legale è anche C++. – Ferruccio

+0

Sembra che sia in C++ 11, buono a sapersi ... –

2

Non si può arrivare ai nomi enum in fase di esecuzione dal momento che tali simboli sono ormai lontani dalla quindi, ma puoi usare costanti multi-carattere per creare valori più significativi per le tue enumerazioni.

#import <Foundation/Foundation.h> 

NSString* debugString(int en) { 
    return [NSString stringWithFormat:@"%c%c%c%c", 
      (en>>24) & 0xff, 
      (en>>16) & 0xff, 
      (en>>8) & 0xff, 
      en & 0xff]; 
} 

typedef enum { 
    kOne = 'One.', 
    kTwo = 'Two.', 
    kThree = 'Thre', 
} MyEnum; 

int main(int argc, const char * argv[]) 
{ 
    @autoreleasepool { 
     NSLog(@"kOne = %@", debugString(kOne)); 
     NSLog(@"kTwo = %@", debugString(kTwo)); 
     NSLog(@"kThree = %@", debugString(kThree)); 
    } 
    return 0; 
} 

stamperà

 
kOne = One. 
kTwo = Two. 
kThree = Thre 

sulla console.

Per impedire a debugString di generare garbage, ogni enum deve essere esattamente quattro caratteri (su OSX comunque). Questo è estremamente compilatore e dipendente dalla piattaforma. È buono per il debug, ma non molto altro.

Ovviamente questo non funzionerà se avete bisogno dell'enumerazione per avere valori o valori specifici che riguardano l'uno con l'altro.

+0

Questa è una buona idea, non so perché avresti dati inutili, a meno che tu non avessi alcun membro che fosse più grande di 16 bit ... –

Problemi correlati