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:

break;

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:

unsigned int flag = 0xFF;
switch(flag)
	{
	case 0x00:
		{
		// first option
		break;
		}
	case 0x0F:
		{
		// second option
		break;
		}
	case 0xF0:
		{
		// third option
		break;
		}
	case 0xFF:
		{
		// fourth option
		break;
		}
	default:
		{
		// throw exception
		}
	}

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:

unsigned int flag = 0xFF;
unsigned int sum = 0;
switch(flag)
	{
	case 0x00:
		{
		// first option
		sum += 0x00;
		}
	case 0x0F:
		{
		// second option
		sum += 0x0F;
		}
	case 0xF0:
		{
		// third option
		sum += 0xF0;
		}
	case 0xFF:
		{
		// fourth option
		sum += 0xFF;
		break;
		}
	default:
		{
		// throw exception
		}
	}

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.?