Kilka wyjaśnień. Pierwsza jest ogólna, druga jest specyficzna dla makr preprocesora C z parametrami:
Kontrola przepływu
Widziałem to w zwykłym kodzie C. Zasadniczo jest to bezpieczniejsza wersja goto, ponieważ możesz się z niej wyrwać, a cała pamięć zostanie poprawnie wyczyszczona.
Dlaczego coś takiego jak gotobyłoby dobre? Cóż, jeśli masz kod, w którym prawie każda linia może zwrócić błąd, ale musisz zareagować na wszystkie z nich w ten sam sposób (np. Przekazując błąd dzwoniącemu po wyczyszczeniu), zwykle łatwiej jest uniknąć if( error ) { /* cleanup and error string generation and return here */ }as pozwala uniknąć powielania kodu oczyszczającego.
Jednak w C ++ masz wyjątki + RAII dokładnie w tym celu, więc uznałbym to za zły styl kodowania.
Sprawdzanie średników
Jeśli zapomnisz średnika po wywołaniu makra podobnego do funkcji, argumenty mogą skurczyć się w niepożądany sposób i skompilować się do prawidłowej składni. Wyobraź sobie makro
#define PRINT_IF_DEBUGMODE_ON(msg) if( gDebugModeOn ) printf("foo");
To jest przypadkowo nazwane jako
if( foo )
PRINT_IF_DEBUGMODE_ON("Hullo\n")
else
doSomethingElse();
„Inne” zostanie uznane za powiązane z elementem gDebugModeOn, więc kiedy foojest false, nastąpi dokładna odwrotność tego, co było zamierzone.
Zapewnienie zakresu dla zmiennych tymczasowych.
Ponieważ do / while ma nawiasy klamrowe, zmienne tymczasowe mają jasno określony zakres, przed którym nie mogą uciec.
Unikanie ostrzeżeń „prawdopodobnie niechcianych średników”
Niektóre makra są aktywowane tylko w kompilacjach do debugowania. Definiujesz je tak:
#if DEBUG
#define DBG_PRINT_NUM(n) printf("%d\n",n);
#else
#define DBG_PRINT_NUM(n)
#endif
Teraz, jeśli użyjesz tego w kompilacji wydania wewnątrz warunku, kompiluje się do
if( foo )
;
Wielu kompilatorów uważa to za to samo, co
if( foo );
Które często jest napisane przypadkowo. Więc dostajesz ostrzeżenie. Do {} while (false) ukrywa to przed kompilatorem i jest przez niego akceptowane jako wskazanie, że naprawdę nie chcesz tutaj nic robić.
Unikanie przechwytywania linii przez warunkowe
Makro z poprzedniego przykładu:
if( foo )
DBG_PRINT_NUM(42)
doSomething();
Teraz, w kompilacji do debugowania, ponieważ zwykle dodawaliśmy również średnik, kompiluje się to dobrze. Jednak w kompilacji wydania zmienia się to nagle w:
if( foo )
doSomething();
Lub wyraźniej sformatowany
if( foo )
doSomething();
Co wcale nie jest tym, co było zamierzone. Dodanie do {...} while (false) wokół makra zamienia brakujący średnik w błąd kompilacji.
Co to oznacza dla PO?
Ogólnie rzecz biorąc, chcesz używać wyjątków w C ++ do obsługi błędów i szablonów zamiast makr. Jednak w bardzo rzadkich przypadkach, gdy nadal potrzebujesz makr (np. Podczas generowania nazw klas za pomocą wklejania tokenów) lub jesteś ograniczony do zwykłego C, jest to przydatny wzorzec.