2013-08-08 9 views
9

Załóżmy, że mam makro zdefiniowane, i używam tego makra wewnątrz if elseCzy mogę używać operatora trójskładnikowego w definicji jako przeciwnej do używania, jeśli?

#include <iostream> 

#define LOG(x) {if (x) std::cout << "What is up" << std::endl;} 

int main(void) { 
    if (true) 
    LOG(true); 
    else 
    false; 
    return 0; 
} 

Teraz jest to trudne przypadek, zdałem sobie sprawę, że w zależności od wcięcia nie mogą być pewne dwuznaczności, o którym „jeżeli "the" else "powinno iść z.

ja wymyślił ten soultion

(some_condition) ? dosomething() : true; 

To rozwiązuje problem, ale nie jestem pewien co repercussion posiadania prawdziwego zdania są. Czy to dobre rozwiązanie, czy też jest lepsze podejście?

EDYCJA: Oto kod, którego użyłem, to nie działa. Sprawdź, czy możesz to naprawić?

+2

Wiele odpowiedzi już, ale gdzie jest "X"? –

+0

Dlaczego tu pojawia się wcięcie? W powyższym warunku, nawet używając powyższego 'makro' jeśli' somebool' jest fałszywe, to nawet nie jest wykonywane 'else'. Jest to 'if-else' z' some_condition' jako kontrolerem pod kontrolką 'if' z' somebool'. Symbol "else" jest powiązany z poprzednim "if", chyba że nie ma separacji za pomocą zakresów. –

+1

Nie ma dwuznaczności na temat "else": idzie on z najbliższym blokiem 'if' w tym samym zakresie, który nie ma jeszcze bloku' else'. Składnia C++ ** nie ** zależy od wcięcia. –

Odpowiedz

15

Należy zdefiniować makro w ten sposób:

#define LOG(X) do { if (some_condition) dosomething(); } while (0) 
+0

Dlaczego 'do' /' while'? – RichieHindle

+6

@RichieHindle zobacz tutaj dla uzasadnienia http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html – ouah

+2

@RichieHindle: Możesz napisać to: 'if (c) LOG (abc); else {tak.} ' – Nawaz

4

Przetwarzanie wstępne jest tekstowy substytucji. Można zapytać kompilator dać wstępnie przetworzonej formie tekstowej (z GCC użytku gcc -C -E)

Twój kod jest rozszerzona do

if (somebool) 
    if (some_condition) dosomething(); 
else 
    somethingelse(); 

więc else stosuje się do testu na some_condition, na pewno nie to, co chcesz.

Sztuką jest zawsze rozszerzać makra podobne do instrukcji do do{ ... }while(0) np.

#define LOG(X) do{ if (some_condition) dosomething(); }while(0) 

NB: pętla while(0) zostanie zoptymalizowany dala przez kompilator!

Właściwie, jeśli X pojawia się zarówno some_condition i dosomething() można użyć rozszerzenia GCC, jak na przykład

#define LOG(X) do {typeof(X) _x=(X); \ 
        if (predicate(_x)) handle(_x); }while(0) 

Jeśli X jest zawsze dostawaliśmy int zastąpić typeof(X) przez int. W C++ 11 można zamiast tego użyć auto.

To spowodowałoby, że wywołanie makra w trybie LOG(i++) zrobi coś bardziej sensownego. (prawdopodobnie nie chcesz, aby inkrementacja została wykonana dwukrotnie).

Jeszcze lepiej, unikaj makr i korzystaj z funkcji wbudowanych, gdy to możliwe.

jeśli pracujesz na ogromnym kodzie źródłowym skompilowanym przez GCC, możesz nawet dostosować kompilator gcc -e.g. dodaj własne własne wbudowane lub pragmy - np. MELT lub inne wtyczki GCC, ale takie podejście wymaga pewnej pracy, więc warto na dużych projektach.

Program wstępny, preprocesor GPP może być skonfigurowany i używany jako preprocesor C/C++ i zapewnia bardziej zaawansowane funkcje.

4

Wcięcie jest dla korzyści ludzi, nie ma znaczenia dla kompilatora. W razie wątpliwości dodaj szelki, które zalecam ci cały czas.

Makra są (zazwyczaj) złe. Czy istnieje jakiś powód, dla którego nie możesz skorzystać z funkcji?

Jeśli wynik (..? ..: ..) nie jest "używany" lub "przechowywany" w dowolnym miejscu, wynik jest po prostu ignorowany, ale funkcja będzie nadal wywoływana. Twój kod powinien więc działać dobrze, chociaż jest to zły i mylący styl.

W przeciwieństwie do innych odpowiedzi, nie zauważyłem braku nawiasów klamrowych w makrze, które złamałoby resztę. Ale nie ma to dla mnie znaczenia, ponieważ nigdy nie używałbym tego makra tylko z tego powodu!

1

można zrobić:

#define LOG(X) ((some_condition) && dosomething()) 

Tutaj używam leniwe ocenę &&: prawa część jest oceniany tylko jeśli some_condition jest prawdą.

Zastrzeżenie: dosomething() nie może powrócić void, w zależności od tego co nie wróci, być może trzeba oddać, aby zapobiec ostrzeżenie kompilatora na &&.

Z drugiej strony, twoje rozwiązanie przy użyciu ? : jest bardzo ładne. Nie ma żadnego problemu z wyrażeniem o losowej wartości (true w twoim przypadku); kompilator zoptymalizuje to ładnie.

0

Czy możesz wykonać test wewnątrz swojego dosomething() lub owinąć go tak, aby wykonał test?

Zakładam, że jest to jakiś rodzaj funkcji logowania, którą chcesz usunąć w pewnych kompilacjach, stąd potrzeba zdefiniowania? Jeśli tak, rozważalibyśmy napisanie tego, o ile to możliwe, tak aby był możliwie jak najcieńszy wrapper nad funkcjami, które faktycznie wdrażasz. Chciałbym to zrobić:

void loggingFunction(x) 
{ 
    if (condition) 
    dosomething(x); 
    else 
    dosummatelse(x); 
} 

#define LOG(x) loggingFunction(x) 

To ma dodatkowy bonus gra ładnie z wyrażeń takich jak: LOG(x++).

0

Jeśli chcesz makra być użyteczny w sytuacji wymagającej oświadczenie i obejmuje każdy rodzaj struktury kontrolnej (for, if, while, ...), a następnie należy owinąć go w do { ... } while(0). Następnie wywołujący dodaje średnik, co powoduje, że pętla wykonuje dokładnie jeden raz. Pozwala to uniknąć niejasności, gdy jest używany w oświadczeniu if/else. (Patrz pytanie 10.4 comp.lang.c FAQ.)

Jeśli chcesz makro być użyteczne w kontekście, który wymaga wyraz, nie można używać if/else (chyba, że ​​używasz rozszerzenie języka jak GCC statement expressions, ale że sprawia, że ​​twój kod jest nieprzenośny). Możesz więc użyć trójskładnikowego operatora warunkowego (?:).

W twoim przypadku, to:

#define LOG(x) {if (x) std::cout << "What is up" << std::endl;} 

można zapisać jako:

#define LOG(x) ((x) ? (std::cout << "What is up" << std::endl), 0 : 0) 

Jak widać, można uzyskać skomplikowane. Musiałem dodać dowolne 0, ponieważ ?: wymaga trzech operandów (w przeciwieństwie do instrukcji if, gdzie else jest opcjonalne). Musiałem również dodać operatora przecinka, aby drugi i trzeci operand były zgodne z typem.

Prawdopodobnie nie jest to warte wysiłku w tym przypadku, ponieważ LOG(...) będzie prawdopodobnie używane tylko w kontekście instrukcji (chyba że zaktualizujesz makro, aby uzyskać znaczący wynik), ale może to być ogólnie użyteczna technika.

Z drugiej strony funkcja inline jest prawdopodobnie lepsza.

+0

Funkcja Inline byłaby całkiem dobra, ale używam zmiennej liczby argumentów i przekazuję je do instrukcji printf w makrze :( – Anonymous