2013-09-01 28 views
6

Prawdopodobnie staram się osiągnąć to, co niemożliwe, ale StackExchange zawsze mnie zaskakuje, więc proszę zrób to:Czy możliwe jest sprawdzanie w czasie kompilacji w trybie ciąg do mapy?

Muszę zamapować nazwę na liczbę całkowitą. Nazwy (około 2k) są unikalne. Nie będzie żadnych dodatków ani usunięć do tej listy, a wartości nie zmienią się w czasie wykonywania.

Implementowanie ich jako zmiennych const int daje mi możliwość sprawdzania istnienia i typu w czasie kompilacji. Również jest to bardzo jasne i pełne w kodzie. Błędy są łatwo zauważalne.

Implementacja ich jako std::map<std::string, int> daje mi dużą elastyczność w budowaniu nazw, które można wyszukiwać za pomocą manipulacji ciągami. Mogę użyć tego, aby nadać ciągom znaków parametry dla funkcji, które mogą wysyłać zapytania do listy dla wielu wartości przez dołączanie przedrostków/końcówek do tego ciągu. Mogę również przechodzić przez kilka wartości, tworząc część liczbową nazwy klucza ze zmiennej pętli.

Teraz moje pytanie brzmi: czy istnieje metoda łączenia obu zalet? Brakujące sprawdzanie podczas kompilacji (szczególnie w przypadku istnienia klucza) prawie zabija drugą metodę dla mnie. (Zwłaszcza, gdy std::map cicho zwraca 0, jeśli klucz nie istnieje, co powoduje trudności w znalezieniu błędów.) Jednak możliwości dodawania pętli i dodawania przedrostków/sufiksów są tak cholernie przydatne.

Wolałbym rozwiązanie, które nie korzysta z żadnych dodatkowych bibliotek, takich jak boost, ale proszę, zasugeruj je jednak, ponieważ i tak będę mógł je ponownie wdrożyć.

Przykładem na to, co zrobić z mapą:

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()]); 
} 

Kolejnym celem jest to, że kod pokazany na main() rutynowych pobytów jak łatwe i szczegółowe, jak to możliwe. (Musi być zrozumiany przez nieprogramistów.) Funkcja init() będzie generowana kodowo przez niektóre narzędzia. Funkcje doSomething(int) są poprawione, ale mogę pisać wokół nich funkcje opakowania. Programy pomocnicze, takie jak checkName(), mogą być bardziej skomplikowane, ale muszą być łatwe do debugowania.

+1

brzmi jak chcesz budować ciągi w czasie wykonywania, ale jakoś się to sprawdzane w czasie kompilacji? –

+0

Istnieją sposoby konwertowania wyliczeń na ich odpowiednie ciągi, więc powinno być możliwe. Chociaż trudno jest zobaczyć, jak jest to czas kompilacji, jeśli budujesz łańcuchy w czasie wykonywania, to: –

+0

'std :: map' z pewnością ma zdolność czy wstawiłeś element. Jednym ze sposobów jest "wstaw". – chris

Odpowiedz

1

Jednym ze sposobów, aby zaimplementować przykład używa enum i Token wklejanie, jak to

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)); 
} 

, czy nie jest to najlepszy zależy od tego gdzie nazwy pochodzą.

Jeśli potrzebujesz wsparcia dla pętli przypadków użycia, a następnie rozważyć

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

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

Mimo że używa preprocesora (który ma własne problemy podczas debugowania), to rozwiązuje problem dość ładnie i tworzy dość szczegółowy i łatwy do zrozumienia kod. Będę musiał sprawdzić w prawdziwym kodzie, czy to naprawdę zaznacza wszystkie pola, ale już to lubię. –

+0

Nie można jednak wykonywać pętli for-loop, ponieważ nadal będzie on dostępny tylko w środowisku wykonawczym. Czy istnieje niezła sztuczka do wykonywania pętli dla preprocesora? –

1

Nie jestem pewien, czy rozumiem wszystkie twoje wymagania, ale jak o czymś takim, bez używania std::map. Zakładam, że masz trzy ciągi: "PIERWSZY", "DRUGI" i "TRZECI", które chcesz chcesz odpowiednio odwzorować na 42, 17 i 37.

#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; 
} 

Wadą jest to, że nie można korzystać z różnych ciągów z LOOKUP. Ale teraz możesz powtórzyć wartości.

+0

Interesujący pomysł. Ogranicza zapętlenie do kolejnych wartości, ale jest to całkiem akceptowalne. Niestety, jest także trudno debugować (spróbuj znaleźć 1276 wartości w tablicy). –

0

Korzystanie wyliczenia zostały zarówno w czasie kompilacji sprawdzanie i można pętli nad nim:

How can I iterate over an enum?

+0

Łącze mówi, że nie mogę wykonać pętli, jeśli używam dowolnych liczb dla ENUM, co będzie miało miejsce w przypadku, gdy użyję enum do zapisania mapy nazwa-do-int. –

1

Może coś takiego (niesprawdzone)?

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

Dobry pomysł, ale w moim przypadku niemożliwy. Złożoność generatora kodu wypełniającego listę musiałaby być ogromnie wzmocniona. Mogę zmodyfikować sposób wyprowadzania każdej linii, ale nie mogę dodawać złożonych decyzji. –