Stampa il testo invece del valore da C enum


89

L'output previsto è che stampi la stringa "Sunday", ecc.?
GalacticCowboy

Risposte:


104

Le enumerazioni in C sono numeri che hanno nomi convenienti all'interno del codice. Non sono stringhe e i nomi loro assegnati nel codice sorgente non sono compilati nel programma, quindi non sono accessibili in fase di esecuzione.

L'unico modo per ottenere ciò che desideri è scrivere tu stesso una funzione che traduca il valore dell'enumerazione in una stringa. Ad esempio (assumendo qui che sposti la dichiarazione di enum Daysoutside of main):

In alternativa, potresti usare un array come mappa, ad es

Ma qui probabilmente vorrai assegnare Sunday = 0nell'enumerazione per sicurezza ... Non sono sicuro se lo standard C richiede ai compilatori di iniziare le enumerazioni da 0, sebbene la maggior parte lo faccia (sono sicuro che qualcuno commenterà per confermare o negare questo ).


3
Aw, mi hai battuto sulla soluzione degli array. : P Ma sì, le enumerazioni iniziano sempre da 0 a meno che non specifichi un valore diverso.
casablanca

1
Se dovessi fare affidamento sull'utilizzo delle enumerazioni come indici, in realtà preferirei numerare esplicitamente ciascuna di esse. Non necessario secondo gli standard, ma come gruppo i compilatori non sono stati esattamente i migliori nel seguire gli standard nella mia esperienza.
jdmichal

3
Lo standard C dice: "Se il primo enumeratore non ha =, il valore della sua costante di enumerazione è 0". Ma non fa male a nulla averlo esplicito.
Michael Burr

17
Non dimenticare che con C99 puoi farlo const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Sai, nel caso in cui i giorni della settimana vengano riordinati, o decidi che il lunedì è il primo giorno della settimana.
Tim Schaeffer

2
@ user3467349 Quella (stringa del preprocessore) trasforma semplicemente il simbolo che segue il # in una stringa. Quindi, sì, #Monday si trasformerebbe in "Monday" ma Days TheDay = Monday; printf("%s", #TheDay);stamperebbe "TheDay".
Tyler McHenry

29

Uso qualcosa del genere:

in un file "EnumToString.h":

quindi in qualsiasi file di intestazione si effettua la dichiarazione enum, day enum.h

quindi in un file chiamato EnumToString.c:

quindi in main.c:

questo genererà "automaticamente" le stringhe per ogni enumerazione dichiarata in questo modo e inclusa in "EnumToString.c"


4
È brutto da leggere, ma non hai la duplicazione dei dati. (A differenza di tutti gli altri.) Sono indeciso sull'opportunità di apprezzare questo.
Kim Reece

1
+1 per la soluzione incredibilmente creativa senza duplicazione dei dati e probabilmente la migliore manutenibilità / flessibilità, ma yech! Penso che preferirei ancora seguire il percorso const char * [].
manifestare il

4
Sì, la manutenibilità è fantastica! È davvero facile aggiornare quando cambiano i giorni della settimana! </sarcasm> A proposito, questo non è nemmeno utile per scopi di localizzazione poiché la mappatura tra le stringhe inglesi ei nomi nel programma è ora codificata dal tuo tentativo di evitare la duplicazione. Almeno con gli altri approcci, è possibile tradurre le stringhe senza modificare ogni occorrenza nei file di origine.
R .. GitHub STOP AIUTO AL GHIACCIO

1
Probabilmente puoi internazionalizzarlo (con qualcosa come gettext) cambiando le istruzioni return in return _(#element)e simili.
Vargas

Quando il preprocessore C è così utile ma così brutto, di solito lo sostituisco con un semplice generatore di codice o un preprocessore personalizzato in un linguaggio di scripting. E in effetti, ho uno script Python che ho usato esattamente per questo scopo in più progetti. Ma non lo uso spesso al giorno d'oggi: per molti casi d'uso puoi cavartela semplicemente usando le stringhe e non preoccuparti delle enumerazioni (e ancora di più in C ++ o ObjC).
abarnert

7

Il modo in cui di solito lo faccio è memorizzare le rappresentazioni di stringa in un array separato nello stesso ordine, quindi indicizzare l'array con il valore enum:


4

enumle s in C non funzionano davvero come ti aspetti. Puoi pensare a loro come a costanti glorificate (con alcuni vantaggi aggiuntivi relativi all'essere una raccolta di tali costanti), e il testo che hai scritto per "domenica" viene davvero risolto in un numero durante la compilazione, il testo è alla fine scartato.

In breve: per fare quello che vuoi veramente dovrai mantenere un array di stringhe o creare una funzione per mappare dal valore dell'enumerazione al testo che desideri stampare.


4

Le enumerazioni in C sono fondamentalmente zucchero sintattico per elenchi denominati di valori interi sequenziati automaticamente. Cioè, quando hai questo codice:

Il tuo compilatore in realtà sputa questo:

Pertanto, l'output di un'enumerazione C come stringa non è un'operazione che ha senso per il compilatore. Se vuoi avere stringhe leggibili dall'uomo per questi, dovrai definire le funzioni per convertire da enumerazioni a stringhe.


4

Ecco un modo più pulito per farlo con le macro:

Non sono sicuro che si tratti di preprocessori b / n totalmente portatili, ma funziona con gcc.

Questo è c99 btw, quindi usalo se lo colleghic99 strict a (il compilatore online) ideone .


Devo amare quanto sono "pulite" le macro :-).
mk12

3

So di essere in ritardo alla festa, ma che ne dici?

In questo modo, non è necessario mantenere sincronizzati manualmente enuml' char*array e. Se sei come me, è probabile che in seguito cambierai il enum, e l' char*array stamperà stringhe non valide. Questa potrebbe non essere una funzionalità universalmente supportata. Ma in fin dei conti, la maggior parte dei compilatori C moderni supportano questo stile iniziale designato.

Puoi leggere di più sugli inizializzatori designati qui .


1

La domanda è che vuoi scrivere il nome solo una volta.
Ho un ider come questo:

#define __ENUM(situation,num) \
    int situation = num;        const char * __##situation##_name = #situation;

    const struct {
        __ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write, 
        __ENUM(get_negative_to_unsigned, -204);
        __ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
        int get_no_num = -201;      const char * __get_no_num_name = "get_no_num";
        int get_float_to_int = -202;        const char * get_float_to_int_name = "float_to_int_name";

    }eRevJson;
#undef __ENUM
    struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
    inline const char * enumRevJsonGetString(int num) {
        sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
        for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
            if (ptr[i].value == num) {
                return ptr[i].p_name;
            }
        }
        return "bad_enum_value";
    }

usa una struttura per inserire enum, in modo che una printer to string possa seguire ogni valore enum definito.

int main(int argc, char *argv[]) {  
    int enum_test = eRevJson.get_other_string;
    printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);

>error is get_other_string, number is -203

La differenza con enum è che il builder non può segnalare errori se i numeri vengono ripetuti. se non ti piace scrivere il numero, __LINE__potresti sostituirlo:

#define ____LINE__ __LINE__
#define __ENUM(situation) \
    int situation = (____LINE__ - __BASELINE -2);       const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
    __ENUM(Sunday);
    __ENUM(Monday);
    __ENUM(Tuesday);
    __ENUM(Wednesday);
    __ENUM(Thursday);
    __ENUM(Friday);
    __ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
    sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
    for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
        if (ptr[i].value == num) {
            return ptr[i].p_name;
        }
    }
    return "bad_enum_value";
}
int main(int argc, char *argv[]) {  
    int d = eDays.Wednesday;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
    d = 1;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
}

>day Wednesday, number is 3 >day Monday, number is 1


0

Sono nuovo a questo, ma una dichiarazione di switch funzionerà sicuramente


Una formattazione corretta (leggi: rientro) deve essere un must per il codice letterale nelle risposte ...
p4010

0

Mi piace che questo abbia enum nel dayNames. Per ridurre la digitazione, possiamo fare quanto segue:


0

C'è un'altra soluzione: creare la propria classe di enumerazione dinamica. Significa che hai una structe qualche funzione per creare una nuova enumerazione, che memorizza gli elementi in a structe ogni elemento ha una stringa per il nome. Hai anche bisogno di un tipo per memorizzare singoli elementi, funzioni per confrontarli e così via. Ecco un esempio:

Le funzioni di enumerazione dovrebbero essere nella loro unità di traduzione, ma le ho combinate qui per renderlo più semplice.

Il vantaggio è che questa soluzione è flessibile, segue il principio DRY, è possibile memorizzare le informazioni insieme a ciascun elemento, creare nuove enumerazioni durante il runtime e aggiungere nuovi elementi durante il runtime. Lo svantaggio è che questo è complesso, necessita di allocazione dinamica della memoria, non può essere utilizzato in switch- case, necessita di più memoria ed è più lento. La domanda è se non dovresti usare una lingua di livello superiore nei casi in cui ne hai bisogno.


-3

TheDay esegue la mappatura su un tipo intero. Così:

Tenta di analizzare TheDay come una stringa e stamperà spazzatura o si bloccherà.

printf non è typesafe e si fida che tu gli passi il valore corretto. Per stampare il nome del valore, è necessario creare un metodo per mappare il valore enum su una stringa: una tabella di ricerca, un'istruzione switch gigante, ecc.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.