2013-09-01 10 views
6

Probabilmente sto cercando di ottenere l'impossibile, ma StackExchange mi sorprende sempre, quindi per favore provate a fare questo:È possibile una mappa da stringa a int per il controllo compilato?

Ho bisogno di mappare un nome ad un intero. I nomi (circa 2k) sono unici. Non ci saranno aggiunte o eliminazioni a quell'elenco e i valori non cambieranno durante il runtime.

L'implementazione come variabili const int mi consente di verificare la durata e il tipo in fase di compilazione. Anche questo è molto chiaro e dettagliato nel codice. Gli errori sono facilmente individuabili.

L'implementazione come std::map<std::string, int> mi dà un sacco di flessibilità per creare i nomi da cercare con la manipolazione delle stringhe. Posso usare questo per dare stringhe come parametri alle funzioni che possono interrogare l'elenco per più valori aggiungendo pre-/suffissi a quella stringa. Posso anche eseguire il loop su più valori creando una parte numerica del nome della chiave dalla variabile loop.

Ora la mia domanda è: esiste un metodo per combinare entrambi i vantaggi? Il controllo in fase di compilazione mancante (in particolare per l'esistenza della chiave) quasi uccide il secondo metodo per me. (Specialmente come std::map restituisce in modo invisibile 0 se la chiave non esiste che crea bug difficili da trovare.) Ma le funzionalità di loop e pre/suffisso sono così dannatamente utili.

Preferirei una soluzione che non utilizzi librerie aggiuntive come boost, ma per favore suggeritele comunque dato che potrei essere in grado di ri-implementarle comunque.

Un esempio su quello che faccio con la mappa:

void init(std::map<std::string, int> &labels) 
{   
    labels.insert(std::make_pair("Bob1" , 45)); 
    labels.insert(std::make_pair("Bob2" , 8758)); 
    labels.insert(std::make_pair("Bob3" , 436)); 
    labels.insert(std::make_pair("Alice_first" , 9224)); 
    labels.insert(std::make_pair("Alice_last" , 3510)); 
} 

int main() 
{  
    std::map<std::string, int> labels; 
    init(labels); 

    for (int i=1; i<=3; i++) 
    { 
    std::stringstream key; 
    key << "Bob" << i; 
    doSomething(labels[key.str()]); 
    } 

    checkName("Alice"); 
} 

void checkName(std::string name) 
{ 
    std::stringstream key1,key2; 
    key1 << name << "_first"; 
    key2 << name << "_last"; 
    doFirstToLast(labels[key1.str()], labels[key2.str()]); 
} 

Un altro obiettivo è che il codice mostrato nelle main() soggiorni di routine come facile e verbose possibile. (Deve essere compreso dai non programmatori.) La funzione init() verrà generata dal codice da alcuni strumenti. Le funzioni doSomething(int) sono fisse, ma posso scrivere attorno a esse le funzioni wrapper. Aiutanti come checkName() possono essere più complicati, ma devono essere facilmente debuggabili.

+1

Sembra che si vuole costruire le stringhe in fase di esecuzione, ma in qualche modo hanno questo controllato a tempo di compilazione? –

+0

Ci sono modi per convertire le enumerazioni nelle loro stringhe appropriate, quindi dovrebbe essere possibile .. Sebbene sia difficile vedere come è il tempo di compilazione se si costruiscono le stringhe in fase di esecuzione –

+0

'std :: map' ha sicuramente la capacità di raccontare se un elemento è stato inserito. Un modo è "inserire". – chris

risposta

1

Un modo per implementare il vostro esempio utilizza un enum e incolla di token, come questo

enum { 
    Bob1 = 45, 
    Bob2 = 8758, 
    Bob3 = 436, 
    Alice_first = 9224, 
    Alice_last = 3510 
}; 

#define LABEL(a, b) (a ## b) 

int main() 
{  

    doSomething(LABEL(Bob,1)); 
    doSomething(LABEL(Bob,2)); 
    doSomething(LABEL(Bob,3)); 
} 


void checkName() 
{ 
    doFirstToLast(LABEL(Alice,_first), LABEL(Alice,_last)); 
} 

Se questo è meglio dipende da dove i nomi provengono.

Se è necessario supportare il ciclo for caso d'uso, quindi prendere in considerazione

int bob[] = { 0, Bob1, Bob2, Bob3 }; // Values from the enum 

int main() 
{  
    for(int i = 1; i <= 3; i++) { 
    doSomething(bob[i]); 
    } 
} 
+0

Anche se si utilizza il preprocessore (che viene fornito con i propri problemi durante il debug), questo risolve il problema piuttosto bene e produce codice abbastanza dettagliato e di facile comprensione. Dovrò controllare all'interno del codice reale se davvero spunta tutte le caselle, ma mi piace già. –

+0

Tuttavia, non è possibile fare for-loops, poiché questo sarebbe ancora disponibile solo in fase di esecuzione. O c'è un trucco non male per fare for-loops con il preprocessore? –

1

Non sono sicuro di aver compreso tutte le vostre esigenze, ma di qualcosa del genere, senza utilizzare std::map. Suppongo che tu abbia tre stringhe, "PRIMO", "SECONDO" e "TERZO" che tu voglia mappare a 42, 17 e 37, rispettivamente.

#include <stdio.h> 

const int m_FIRST = 0; 
const int m_SECOND = 1; 
const int m_THIRD = 2; 

const int map[] = {42, 17, 37}; 

#define LOOKUP(s) (map[m_ ## s]) 

int main() 
{ 
    printf("%d\n", LOOKUP(FIRST)); 
    printf("%d\n", LOOKUP(SECOND)); 
    return 0; 
} 

Lo svantaggio è che non è possibile utilizzare stringhe variabili con LOOKUP. Ma ora puoi scorrere i valori.

+0

Idea interessante. Limita il loop a valori consecutivi, ma in realtà è abbastanza accettabile. Sfortunatamente, è anche difficile eseguire il debug (prova a trovare il 1276esimo valore nell'array). –

0

Utilizzando enum avete sia a tempo di compilazione di controllo e si può ciclo su di esso:

How can I iterate over an enum?

+0

Il link dice che non posso passarci sopra se uso numeri arbitrari per ENUM, il che sarebbe il caso se uso l'enum per memorizzare una mappa da nome a int. –

1

Forse qualcosa di simile (non testato)?

struct Bob { 
    static constexpr int values[3] = { 45, 8758, 436 }; 
}; 

struct Alice { 
    struct first { 
     static const int value = 9224; 
    }; 
    struct last { 
     static const int value = 3510; 
    }; 
}; 

template <typename NAME> 
void checkName() 
{ 
    doFirstToLast(NAME::first::value, NAME::last::value); 
} 

...

constexpr int Bob::values[3]; // need a definition in exactly one TU 

int main() 
{ 
    for (int i=1; i<=3; i++) 
    { 
     doSomething(Bob::values[i]); 
    } 

    checkName<Alice>(); 
} 
+0

Buona idea, ma non è possibile nel mio caso. La complessità del generatore di codice che riempie la lista dovrebbe essere enormemente aumentata. Posso modificare l'output di ogni riga, ma non posso aggiungere complesse decisioni. –

Problemi correlati