Jak nie należy używać operatorów

2008-04-30 22:23

W wielu API funkcje mają bardzo prosty sposób powiadamiania o tym, czy ich wykonanie zakończyło się sukcesem czy porażką. Albo więc wykorzystują typ bool bezpośrednio, albo wpasowują się w konwencję, iż niezerowa wartość liczbowa jest tożsama z prawdą, a zero z fałszem. To sprawia, że możliwe jest pisanie warunków podobnych do poniższego:

  1. if (!RegisterClassEx(&wc))    Error("Can't register window class.");

Ładne to i opisowe – wręcz samodokumentujące się. Ale czasami tak się zrobić nie da, bo wartości zwracane nie chcą współpracować z tym modelem.

Przykład? To większość biblioteki runtime języka C oraz API systemów uniksowych. O ile tylko rezultatem funkcji należącej do któregoś z tych dwóch zbiorów nie jest wskaźnik, konwencja informowania o powodzeniu lub niepowodzeniu jest zwykle dość osobliwa. Według niej zero oznacza sukces, natomiast porażka wykonania jest sygnalizowana przez wartość mniejszą od zera – zazwyczaj -1. Oczywiście nijak nie pasuje to sposobu interpretowania liczb jako wartości logicznych. Sprawia to, że sprawdzanie rezultatu takich funkcji może wyglądać cokolwiek enigmatycznie:

  1. if (close(fd) < 0)    perror("closing file");&#91;/cpp]
  2. Ale nie wszystko stracone :) W przypadku funkcji typu boolowskiego możemy posłużyć się operatorem logicznej negacji (<code>!</code>), dzięki czemu zawierające je <code>if</code>y są całkiem przejrzyste. Okazuje się, że z powodu pewnego zbiegu okoliczności także te wspomniane przed chwilą funkcje można potraktować tak samo... o ile dodamy jeszcze jeden operator. A dokładniej - jeśli oprócz negacji logicznej dodamy też bitową (<code>~</code>):
  3. [cpp]if (!~close(fd))    perror("closing file");

Powód, dla którego to działa, jest dość prosty. W standardowym sposobie zapisu liczb całkowitych, stosowanym na zdecydowanej większości typowych i nietypowych maszyn (zwanym uzupełnieniem do 2 – U2), wartość -1 to w zapisie binarnym same jedynki. Negując je bitowo, otrzymujemy same zera – czyli zero, a więc logiczny fałsz. A odwrotnością fałszu jest oczywiście prawda i wszystko działa poprawnie. Wygląda więc tak, jakby skromna tylda zdołała “naprawić” funkcję, by zachowywała się zgodnie z oczekiwaniami…

Tylko czy aby na pewno nowy zapis jest bardziej sugestywny? Mam nadzieję, że każdy potrafi poprawnie odpowiedzieć na to pytanie we własnym zakresie :) Na koniec jednak muszę – dla spokoju sumienia – ostrzec wszystkich: zdecydowanie nie róbcie tego w domu :D

Tags: , ,
Author: Xion, posted under Programming »


4 comments for post “Jak nie należy używać operatorów”.
  1. moriturius:
    May 1st, 2008 o 6:36

    Zdarza się że funkcje takie zwracają różne kody błędów mniejsze od 0. Dla -1 to faktycznie działa ale dla -2 już niebardzo :)

  2. jarek:
    May 1st, 2008 o 9:20

    Bardzo podobnie do sprawdzanych magicznych wartości zwracanych przez f(). Używane są warunki w ifach
    if( a && b*a && f()|c ||a+2<0 )
    żyj();
    else
    foo();

    Przy tworzeniu takich baboli, każdy myśli sobie: jestem zajebisty, to po co mam pisać nawiasy/komentarze/etc. Lubie oglądać ludzi i ich miny jak muszą własne wypociny później poprawiać.

    Dobrze by było, gdyby oprócz programowania uczyli także dobrego stylu programowania.

  3. Xion:
    May 1st, 2008 o 14:00

    Tak jest, to doskonały przykład na to, jak *nie* należy programować, bowiem ten przykład jest:

    1) Ryzykowny – jak słusznie zauważył moriturius, działa tylko dla wartości -1. Akurat np. funkcje API uniksowego spełniają ten warunek, ale jeśli ktoś pomyśli sobie na przykład, aby w liczbie ujemnej zapisywać kod błędu, to już tak dobrze nie będzie.

    2) Nieprzenośny – opiera się na fakcie, że -1 w kodzie U2 to 11..11. Jeśli mielibyśmy do czynienia na przykład z reprezentacją przy użyciu bitów znaku, to -1 miałby postać 100..001, co po negacji oczywiście nie daje zera.

    3) Nieprzejrzysty – bez uważnego wpatrzenia się i/lub odwołania do dokumentacji, bardzo ciężko (bez komentarzy) zrozumieć, “co autor miał na myśli”.

    4) Nie dający żadnych korzyści – pierwotne porównanie z 0 czy -1 jest po prostu lepsze.

    Tak więc jedyną zaletą tego przykładu jest to, że świetnie nadaje się na zły przykład i w takim też charakterze go zaprezentowałem.

  4. Tarains:
    May 1st, 2008 o 14:23

    Inaczej mówiąc jest super! :D

Comments are disabled.
 


© 2017 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.