Tam, gdzie kończy się C++

2007-07-20 19:26

Pod jednym z ostatnich postów, dotyczącym kwestii rysowania dwuwymiarowych sprite’ów, rozpętała się całkiem ciekawa dyskusja. Jej głównym tematem było to, czy i jak implementowany system 2D będzie przydatny w renderowaniu elementu niezbędnego każdej grze – choćby była do bólu trójwymiarowa. Chodzi naturalnie o graficzny interfejs użytkownika, czyli GUI.

Wyświetlanie elementów takiego interfejsu (na który składać się będą różnego rodzaju kontrolki, jak przyciski, pola tekstowe czy listy rozwijalne) to dość złożone zagadnienie. Można je rozwiązać łatwo acz nieefektywnie lub trochę trudniej, ale prawie optymalnie :) Oprócz tego GUI prezentuje sobą też bardzo poważne wyzwanie projektowe, które wymaga dokładnego zaplanowania występujących w nim klas i ich składowych.
Niestety w C++ dochodzi do tego jeszcze jeden problem, który w dodatku jest nieco krępujący. Nie lubimy się nim chwalić w towarzystwie i w sumie chcielibyśmy o nim zapomnieć, zakopać jak najgłębiej, by tylko usunąć go z pola widzenia. Ale rzeczywistość skreczy i uciekanie od kłopotu go nie rozwiąże… Trzeba wypowiedzieć go na głos. Otóż – w C++ nie ma delegatów i w związku z tym obsługa zdarzeń generowanych przez kontrolki GUI zaczyna być problemem.

‘Delegat’ to taka trochę myląca dla prostego i wręcz niezbędnego w obiektowym języku programowania mechanizmu. Delegatem nazywamy bowiem pewien rodzaj wskaźnika, który pokazuje na konkretną metodę w konkretnym obiekcie. Taki wskaźnik jest konieczny do wygodnego realizowania mechanizmu callback, czyli powiadamiania (poprzez wywołanie owej metody), że coś się zdarzyło.
W programowaniu strukturalnym nie ma z tym problemu, jako że istnieją wskaźniki na zwykłe funkcje. Najbardziej znanym jest chyba wskaźnik na procedurę zdarzeniową okna w Windows API:

  1. typedef LRESULT (CALLBACK *WNDPROC)(HWND, UINT, WPARAM, LPARAM);

Mimo pokrętnej składni, wskaźniki do funkcji dobrze spełniają swoje zadanie. Problem polega na tym, że w programowaniu obiektowym nie życzymy sobie obecności “luźnych” funkcji, skoro wszystko zamykamy w klasy. Ten rodzaj wskaźników jest więc kompletnie nieprzydatny.

C++ ma też inny rodzaj wskaźników na kod. Są to w gruncie rzeczy bardzo dziwne twory, dla których samodzielnego zastosowania póki co nie udało mi się znaleźć (co oczywiście nie znaczy, że ono nie istnieje). Te wskaźniki służą do pokazywania na metodę o danej sygnaturze, lecz nie w obiekcie, tylko w klasie. Jak już mówiłem, są to bardzo wyjątkowo nietypowe twory (C++ jest chyba jedynym językiem posiadającym coś podobnego), a towarzysząca im składnia jest zdecydowanie odpychająca.
Ten egzotyczny mechanizm można jednak użyć do implementacji własnego systemu delegatów w C++. Kiedyś nawet popełniłem artykuł wyjaśniający, jak można to zrobić. Przedstawiony tam sposób jest jednak mało elegancki i dlatego jest raczej wątpliwe, bym bo użył.

Skłaniam się raczej do zastosowania zewnętrznej biblioteki, co jest według mnie dość przykrą koniecznością. Ciężko bowiem pogodzić się z tym, że używany język programowania zmusza do uciekania się do zewnętrznych i nieustandaryzowanych rozwiązań tylko po to, by w jakiś w miarę sensowny sposób symulować to, czego mu ewidentnie brakuje. W dodatku kłóci się to z moją filozofią konstrukcji kodu “bibliotecznego” (do którego należy silnik graficzny lub silnik gry), która każe eliminować zewnętrzne powiązania. Czasem jednak stajemy pod ścianą.
Opcje są właściwie dwie. Pierwsza to użycie bibliotek Function i Bind wchodzących w skład Boosta. Mimo że Boost jest rzecz jasna świetnie opracowanym zestawem bibliotek rozszerzających C++, chciałbym jednak jej uniknąć ze względów wydajnościowych. Dokładniej chodzi o obciążenie kompilatora (z którego właściwie każda część Boosta wyciska ostatnie soki) oraz na znacznie ważniejszą kwestię wywołań delegatów w czasie wykonania programu. Dlatego skłaniam się raczej ku innej bibliotece o nazwie FastDelegate, która pod tym względem nie ma sobie równych. Być może kiedyś uda mi się napisać coś równie dobrego i będę mógł z niej zrezygnować, ale chyba bardziej prawdopodobne jest, że do tego czasu C++ będzie miał już wbudowany mechanizm delegatów :)

Mógłbym jeszcze wspomnieć o innym rozwiązaniu polegającym na użyciu polimorfizmu i funkcji wirtualnych i o tym, dlaczego w C++ – ze względu na brak niestatycznych klas wewnętrznych – byłoby ono skrajnie niewygodne. Myślę jednak, że na dzisiaj moglibyśmy już sobie tego oszczędzić :D

Be Sociable, Share!
Be Sociable, Share!
Tags: ,
Author: Xion, posted under Programming »


2 comments for post “Tam, gdzie kończy się C++”.
  1. Tarains:
    July 21st, 2007 o 15:15

    Ja do obsługi zdarzeń w swoim module GUI korzystam po prostu ze swojego języka skryptowego.

  2. ZiomisławPaliblant:
    October 25th, 2007 o 23:56

    Czyli dalej nie wiadomo co zrobić, jeśli człowiek nie chce używać żadnych third-party code…

Comments are disabled.
 


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