2013-03-29 18 views
5

muszę napisać sam kod zależny od kompilacji stałym parametrem, coś jak:Makro powtórzyć prawie taki sam kod

map["text 0"] = vec[0]; 
map["text 1"] = vec[1]; 
... 
map["text n"] = vec[n]; 

Problem polega na tym, że nie wiem n kiedy piszę kod , Otrzymuję go jako parametr szablonu. Oczywistym rozwiązaniem jest użycie jednej pętli i wygenerowanie "text k" wewnątrz pętli i użycie vec[k], ale ma to wpływ na środowisko wykonawcze, kiedy powinno to być zrobione w czasie kompilacji. Innym rozwiązaniem byłoby specjalizacja funkcji dla różnych wartości N, ale w ten sposób będę musiał napisać ten sam kod ręcznie kilka razy i nie ma powodu, aby go szablon.

Wiem, że istnieją pewne inteligentne makra, które mogą powtarzać podobne rzeczy N razy (jak rodzina makr BOOST_PP_REPEAT), ale nie mogę znaleźć jednego rozwiązania dla mojego konkretnego problemu.

Czy masz jakieś rozwiązanie tego problemu?

+0

można używać C++ 11? – jrok

+0

@jrok Tak, ale nie wszystkie funkcje (VS2010 i najnowszy kod Xcode) – Felics

+0

Prawdopodobnie szablony i pętle mogły zostać zoptymalizowane przez kompilator. –

Odpowiedz

4

Jeśli naprawdę nie masz poważnych ograniczeń wydajności, nie ma powodu, aby martwić się o czas wykonywania. Wstawki będą i tak wykonywane w czasie wykonywania, a czas wstawiania zdecydowanie zdominuje czas wymagany do zmiany znaku w łańcuchu.

Ponadto, makra są trudne do debugowania i konserwacji: unikaj ich, jeśli to możliwe. Tutaj chciałbym zaproponować, aby rozwinąć prostą pętlę:

std::string s = "text 0"; 
std::map<std::string, int> m; 
for (int i = 0; i < N; i++) 
{ 
    m[s] = vec[i]; 
    s[5] = '1' + i; // This is going to be the run-time overhead... 
} 

Jeśli numery rosną wyższe niż 9 w C++ 11 można korzystać z funkcji to_string() do konwersji liczby całkowitej na ciąg znaków:

std::string const s = "text "; 
std::map<std::string, int> m; 
for (int i = 0; i < N; i++) 
{ 
    m[s + std::to_string(i)] = vec[i]; 
} 

Jeśli wydajność okaże się problemem, możesz spróbować bardziej hardcoreowego podejścia opartego na makrach. Jeśli jednak twoje pomiary nie będą wskazywać na znaczny narzut, preferują prostotę i klarowność i rozwijają prostą pętlę.

+0

Spowoduje to niezdefiniowane zachowanie, nie będę mógł modyfikować znaku stałego * - "tekst 0" to const char *, nawet jeśli nie jest zadeklarowany jako const. – Felics

+1

@Felics: Literał łańcuchowy '" text 0 "' ma typ 'const char []', ale to właśnie używasz do zainicjowania tablicy 'char', która jest modyfikowalna. Nie modyfikujesz oryginalnego tekstu literackiego, więc nie ma tu UB. A jeśli twój wzorzec jest bardziej złożony, możesz użyć 'std :: string'. –

+0

Nawet jeśli zadeklarujesz to jako non const, istnieje konwersja ze stałej do innej niż stała (zaakceptowana przez standard 2003, nie zaakceptowana przez standard 2011). Również N może mieć więcej niż jedną cyfrę. – Felics

0

wierzę dodaje powinno działać:

#include <boost/preprocessor.hpp> 
//... or just the required sub-headers 

// Will generate code for 0, 1, ... (N_END - 1) 
#define N_END 10 

#define ONE_ASSIGNMENT(maZ, maIter, maData) \ 
    if (maIter <= n) map["text " BOOST_PP_STRINGIZE(maIter)] = vec[maIter]; 

BOOST_PP_REPEAT(N_END, ONE_ASSIGNMENT, %%) //this generates the code 

#undef ONE_ASSIGNMENT 
#undef N_END 

Zauważ, że if() porównuje dosłownym do parametru szablonu (n), więc wszelkie optimiser wart jego sól będzie generować branchless kod z tego.

Używam %% jako "ta wartość nigdy nie jest używana." Jest przekazywany do argumentu maData, więc jeśli masz coś użytecznego do przekazania (np. "text "), możesz to zrobić zamiast tego.

+0

Należy zauważyć, że "n" jest odbierane jako parametr szablonu ... –

+0

@MatthieuM. Tak. W porządku. Nawet polegam na tym, aby optymalizator zmienił 'if()' w samo ciało lub 'nop', ponieważ zna zarówno literalną (' maIter'), jak i stałą czasu kompilacji ('n') w stanie. Oczywiście kod działałby również bez optymalizacji (ale tak naprawdę to po prostu eliminacja stanu kompilacji). – Angew

+0

Wtedy nie do końca rozumiem, dlaczego '<= n', nie powinno być' == n'? (Nie jestem pewien, czy zrozumiałem pytanie anwyay ...) –

0

Powtórzenie nie wydaje się być dla mnie problemem, ale kompilacja-czas konwersji int na ciąg i kolejne konkatenacje ma. Problem można rozwiązać powtórzenie wzdłuż poniższej techniki (nietestowanego):

template<k,l> struct fill_vector { 
    static void doIt (... & vec) { 
     vec [INT_TO_TEXT (k)] = k; 
     fill_vector<k+1,l-1>::doIt (vec); 
    } 
}; 

template<k> struct fill_vector<k,0> { 
    static void doIt (... & vec) { 
     vec [INT_TO_TEXT (k)] = k; 
    } 
}; 

//... 

fill_vector<0,n>::doIt (vec); 

Może ktoś ma pomysł jak wdrożyć INT_TO_TEXT

Powiązane problemy