This post comes from the first version of this blog.
Please send me an email if anything needs an update. Thanks!
Wpisy dotyczące języka C++ pojawiają się dosyć rzadko na moim blogu, pomimo tego, iż uważam go za najwspanialszy język w jakim miałem okazję pisać kod [a trochę ich już poznałem]. Tym razem pomysł na kolejnego posta wpadł mi do głowy niepostrzeżenie podczas tradycyjnej, oczywiście nocnej, sesji programistycznej. Ze względu na czytelność [i jak zwykle wydajność ;]] produktu końcowego, zdecydowałem podczas projektowania, że będę korzystał z instrukcji switch zamiast serii else-if’ów. Zadowolony z upieczenia “dwóch pieczeni na jednym ogniu” przystąpiłem więc do konwersji moich pomysłów na formę binarną.
Po napisaniu pewnej [działającej już] części projektu przyszedł czas na przetestowanie. Kompilacja, uruchomienie, analiza wyników programu… błąd. Wyglądało na to, że jedna z opcji w switch’u po prostu się nie wykonuje. Krótkie spojrzenie na kod - faktycznie, obsługa ograniczała się jedynie do krótkiego:
|
|
co nawet przy najlepszych chęciach nie da zbytnio powalających efektów. ;] Ze względu na to, że obie opcje realizowałyby praktycznie tą samą logikę, dotarło do mnie w pewnej chwili, że musiałbym po prostu skopiować zawartość jednego z case’ów w nowe miejsce i tym samym zduplikować fragment źródła, co nie było zbyt szczęśliwym pomysłem. Oczywiście przyszedł mi na myśl pomysł przepisania całego switcha na konstrukcję if / else if, gdzie mógłbym użyć operatorów logicznych do połączenia warunków, jednak to też średnio mi się podobało. Należało więc stworzyć rozwiązanie korzystające z istniejącej struktury kodu i rozwiązujące problem zarówno algorytmicznie jak i ideowo.
Potrzebowałem paru minut, aby zrozumieć, że gotowy sposób leży na dłoni [nocne kodowanie jest fajne, ale do czasu]. Należy pamiętać, że wszystkie opcje case instrukcji switch są wykonywane jedna po drugiej, aż do natrafienia na przerywający sekwencję break lub koniec switch’a. W moim przypadku dotarcie do rozwiązania utrudniała zła kolejność [nielogiczna z punktu widzenia logiki programu] opcji case. Po zamianie na odpowiednią, bardziej czytelną dla programisty, “solucja” nasunęła się sama. Otóż:
Przy odpowiednim ustawieniu opcji możemy pominąć przerywające break’i we wszystkich “wyższych” niż ta, w której chcemy umieścić właściwy kod. Wtedy przy uruchomieniu kod zawarty we wszystkich złączonych w ten sposób case’ach zostanie wykonany tak, jakbyśmy napisali go w jednym bloku [analogia do instrukcji if wraz z warunkami oddzielonymi operatorem “lub” [or, “||”] jest odpowiednia]. Najlepiej będzie, jeśli pokażę to na przykładzie:
|
|
Mamy tutaj standardowy przykład - wykonuje się tylko jedna z opcji w switch’u, jeśli wartość zmiennej nigdzie nie pasuje, wykonywany jest blok default. W naszym przypadku pierwsze trzy zostaną pominięte, wykona się blok dla 0xFF, potem break “wyskoczy” nam z całej instrukcji. A co, jeśli np. chcielibyśmy zsumować wartości wszystkich wybranych opcji? Przykład wzięty kompletnie z kosmosu, ale od czego są programiści. ;] Spróbujmy więc:
|
|
Ze względu na pominięcie break’a w każdej opcji oprócz ostatniej [inaczej dostalibyśmy hipotetyczny wyjątek rzucony w bloku default], zostanie wykonany kod dla wszystkich opcji powyżej 0xFF, wraz z tym który jest w samym 0xFF. Na końcu otrzymamy więc sumę [zmienna “sum”] zawierającą liczby od pierwszej “złapanej” opcji aż do końca switch’a.
Oczywiście można wykorzystać ten fakt w dowolny sposób, mi pozwolił on na oszczędzenie rozmiaru kodu, ze względu na to, że nie musiałem go [tego kodu] parę razy kopiować, po prostu “umożliwiłem” kilku kolejnym opcjom na gładkie przejście z jednej do drugiej.
W tym miejscu powstaje standardowe pytanie do Was, czytelników - co o tym sądzicie? Czy znacie jakieś inne ciekawe i niestandardowe sposoby użycia elementów języka [nie tylko C++], funkcji, itp.?