R04-04.DOC

(233 KB) Pobierz
Szablon dla tlumaczy

Rozdział 4.

Tworzenie przenośnego kodu

W niniejszym rozdziale zajmiemy się problematyką dostosowania do wymogów Delphi 6 aplikacji stworzonych za pomocą wcześniejszych wersji Delphi. Rozpoczniemy od przedstawienia najważniejszych różnic pomiędzy kolejnymi wersjami Delphi i wynikających z tych różnic konsekwencji związanych z unowocześnianiem aplikacji. Ponieważ Delphi 6 przełamuje tradycyjne granice środowiska Windows umożliwiając tworzenie aplikacji także dla platformy linuksowej (w środowisku o nazwie Kylix), w dalszym ciągu rozdziału zajmiemy się cechami specyficznymi tej platformy i przedstawimy kilka użytecznych wskazówek dotyczących budowy aplikacji przenośnych pomiędzy obydwoma środowiskami. Końcową część rozdziału poświęcimy niektórym subtelnym różnicom występującym pomiędzy poszczególnymi wersjami Delphi, z uwzględnieniem wpływu tych różnic na zachowanie przenośności aplikacji pomiędzy wersjami.

Mimo iż firma Borland, opracowując nowsze wersje swych produktów, stara się czynić to w miarę bezboleśnie dla stworzonych wcześniej aplikacji, to jednak niektóre unowocześnienia wymagają pewnych ingerencji w istniejący kod źródłowy aplikacji, by zapewnić ich zgodność z najnowszymi wersjami narzędzi programistycznych. Wydaje się to jednak całkowicie zrozumiałe — to rozsądna cena postępu.

Założenia ogólne

Różnice występujące pomiędzy poszczególnymi wersjami Delphi, a także pomiędzy Delphi a Kyliksem, czy też C++Builderem powodują, iż kod źródłowy konkretnej aplikacji musi wyglądać mniej lub bardziej odmiennie w każdym z wymienionych środowisk. Z kolei względy niezawodności oprogramowania — jak również względy samej wygody programistów — przemawiają za tym, by w każdym z tych środowisk kod wynikowy generowany był na podstawie tego samego, bazowego kodu źródłowego. Zobaczmy, jak na gruncie Delphi udało się pogodzić te pozornie sprzeczne wymagania.

Która wersja?

Podstawowym środkiem umożliwiającym wykorzystanie pojedynczego egzemplarza kodu źródłowego w różnych środowiskach projektowych są symbole kompilacji warunkowej. Firma Borland stosuje spójną numerację wersji Delphi i C++Buildera (a odtąd — także Kyliksa) w tym sensie, iż w konkretnej wersji każdego z tych produktów obowiązujący jest dokładnie jeden symbol kompilacji warunkowej o postaci VERxxx, zgodnie z poniższą tabelą:

Tabela 4.1. Symbole kompilacji warunkowej odzwierciedlające wersję produktu

Produkt

Symbol

Delphi 1

VER80

Delphi 2

VER90

C++Builder 1

VER95

Delphi 3

VER100

C++Builder 3

VER110

Delphi 4

VER120

C++Builder 4

VER120

Delphi 5

VER130

C++Builder 5

VER130

Kylix 1

VER140

Delphi 6

VER140

 

Dzięki wymienionym symbolom możliwe jest dedykowanie wybranych fragmentów kodu źródłowego konkretnym wersjom produktu, na przykład:

 

{$IFDEF VER80}

  …

  { kod dla Delphi 1 }

  …

{$ENDIF}

{$IFDEF VER90}

  …

  // kod dla Delphi 2

  …

{$ENDIF}

{$IFDEF VER95}

  …

  // kod dla C++Buildera 1

  …

{$ENDIF}

{$IFDEF VER100}

  …

  // kod dla Delphi 3

  …

{$ENDIF}

{$IFDEF VER110}

  …

  // kod dla C++Buildera 3

  …

{$ENDIF}

{$IFDEF VER120}

 

  // kod dla Delphi 4 i C++Buildera 4

  …

{$ENDIF}

{$IFDEF VER130}

  …

  // kod dla Delphi 5 i C++Buildera 5

  …

{$ENDIF}

{$IFDEF VER140}

 

  // kod dla Delphi 6 i Kyliksa

  …

{$ENDIF}

Notatka

Zagadkowe na pozór numerowanie wersji rozpoczynające się od VER80 wynika z faktu, iż Delphi stanowi kontynuację linii rozwojowej Turbo Pascala — ostatnia jego wersja nosiła numer 7, a obowiązującym na jej gruncie symbolem kompilacji warunkowej był VER70.

Moduły, komponenty i pakiety

Różnice pomiędzy poszczególnymi wersjami Delphi przekładają się bezpośrednio na różnice w formacie skompilowanych modułów — każda wersja produktu generuje specyficzne dla siebie pliki *.dcu nie dające się użyć w innych wersjach (wcześniejszych lub późniejszych). Jeżeli więc dla niektórych modułów tworzonej aplikacji brak jest kodu źródłowego, a aplikacja ta ma być kompilowana w różnych wersjach Delphi, musimy dysponować odrębnym zestawem tych modułów dla każdej z wersji. Nabiera to szczególnego znaczenia w przypadku rozpowszechniania niezależnych komponentów bez udostępniania ich kodu źródłowego — jednak nawet udostępniony kod źródłowy może być niekiedy przydatny tylko w konkretnej wersji Delphi. Przechodząc do nowej wersji, powinniśmy podjąć próbę uzyskania odpowiednich dla tej wersji binariów lub odpowiednio przystosowanego kodu źródłowego.

Notatka

Różnicowanie formatów plików *.dcu w poszczególnych wersjach Delphi jest zjawiskiem tej samej natury, co różnicowanie formatów plików wynikowych *.obj w różnych wersjach języka C++. Należy ponadto pamiętać o tym, iż konieczność ponownego skompilowania modułu *.dcu nie musi wynikać ze zmiany wersji kompilatora — równie prawdopodobną przyczyną mogą być zmiany dokonane w ramach biblioteki VCL.

Wersja Delphi 3 przyniosła ideę pakietu, stanowiącego zespół powiązanych ze sobą modułów źródłowych. Biblioteki komponentów przestały być odtąd masywnymi bibliotekami DLL, a stały się kolekcjami pakietów. Podobnie jak moduły *.dcu, także pakiety związane są ściśle z wersją produktu, pod kontrolą którego zostały utworzone; wymagają więc ponownej kompilacji przy zmianie wersji kompilatora.

Zmiany w środowisku IDE

Zmiany w szczegółach funkcjonowania IDE są bodaj najwcześniej zauważalne dla użytkownika. Oto kilka przykładów niezgodności tego rodzaju pomiędzy różnymi wersjami Delphi:

·         Pliki *.RSM zawierające informację symboliczną dla debuggera, utworzone w poprzednich wersjach Delphi nie zawsze są czytelne dla Delphi 6; podczas próby ich wczytywania otrzymamy komunikat „Error reading symbol file”.  Przeprowadzając kompilację zupełną aplikacji (Build) spowodujemy utworzenie nowych, poprawnych plików *.RSM.

·         Począwszy od Delphi 5 pliki *.dfm nowo tworzonych formatów mają postać tekstową, nieczytelną dla wcześniejszych wersji Delphi. Ten stan rzeczy możemy oczywiście zmienić, likwidując zaznaczenie opcji „New Forms As Text” na karcie Preferences opcji środowiska. Istniejące pliki *.dfm nie są podczas edycji konwertowane na postać tekstową, niezależnie od stanu wspomnianej opcji.

·         Automatyczne generowanie kodu źródłowego podczas tworzenia lub importowania bibliotek typu (Type-Info Libraries) różni się niektórymi szczegółami w kolejnych wersjach Delphi. Podobnie jak w Delphi 5, ustawienia związane z odwzorowaniem nazw symboli na postać czytelną dla debuggera znajdują się w pliku tlibimp.sym; znaczenie tych ustawień opisane jest w systemie pomocy Delphi pod hasłem Mapping symbol names in the Type Library.

Zgodność pomiędzy Delphi i Kyliksem

Zakładając jakikolwiek stopień przenośności tworzonej aplikacji pomiędzy Delphi a Kyliksem, powinniśmy przede wszystkim uświadomić sobie podstawowy fakt, iż biblioteki VCL są tworami specyficznymi dla technologii Windows. Na użytek aplikacji przeznaczonych dla innych środowisk należy posłużyć się więc biblioteką zawierającą komponenty dla tzw. platformy X (CLX — Component Library for X-platform) obsługiwanej w ramach Delphi 6 i Kyliksa. Biblioteka CLX opisana jest ze szczegółami w rozdziale 13.; jej zawartość można podzielić na cztery podstawowe grupy:

 

·         BaseCLX — realizuje mechanizmy podstawowe dla wszystkich komponentów.

·         DataCLX — udostępnia technologię dbExpress zapewniającą szybki dostęp do danych i efektywne zarządzanie nimi. Szczegóły tej technologii opisane są w rozdziale 8.

·         NetCLX — zawiera komponenty i kreatory niezbędne do tworzenia sieciowych klientów i serwerów dla Windows i Linuksa. Tworzenie aplikacji internetowych jest teraz jeszcze łatwiejsze i efektywniejsze niż na gruncie technologii WebBroker w poprzednich wersjach Delphi.

·         VisualCLX — stanowi podstawę do tworzenia międzyplatformowych interfejsów użytkownika (GUI). Mimo iż zewnętrznie podobna do VCL, zrealizowanej jak wiadomo na bazie Windows API, VisualCLX powstała na podstawie biblioteki Qt firmy Troll Tech (http://www.trolltech.com). Biblioteka Qt udostępnia mechanizmy wizualne dla wielu platform, między innymi Windows i Linuksa.

 

Gdy rozpoczniemy tworzenie aplikacji międzyplatformowej CLX — za pomocą opcji File|New|CLX Application menu głównego IDE — i przeanalizujemy zawartość listy uses w utworzonym module formularza głównego, możemy zaobserwować wiele nazw modułów rozpoczynających się od litery Q (QGraphics, QControls, QForms itp.). Są to międzyplatformowe odpowiedniki podobnie nazwanych modułów biblioteki VCL.

Notatka

Choć biblioteka CLX w obecnej wersji umożliwia tworzenie aplikacji jedynie dla Windows i Kyliksa, planuje się jej rozszerzenie w przyszłych wersjach — na wzór biblioteki Qt, dostarczającej mechanizmów dla ponad tuzina różnych platform.

 

Nie w Linuksie

Jest zrozumiałe, iż na platformie linuksowej nie znajdziemy specyficznych dla Windows technologii ADO, COM/COM+, BDE, MAPI itp., należy więc w aplikacjach międzyplatformowych unikać używania modułów związanych z tymi technologiami (Windows, ComObj, ComServ, ActiveX, AdoDb itp.) oraz funkcji specyficznych dla platformy Win32 API, jak np. RaiseLastWin32Error() czy Win32Check(). Ponadto niektóre technologie dostępne w Delphi 6 nie są jeszcze dostępne w obecnej wersji Kyliksa (Kylix 1); planuje się ich wprowadzenie do jego następnych wersji — mowa tu między innymi o mechanizmach DataSnap, BizSnap (SOAP) i WebSnap.

Różnice między kompilatorami

Mimo iż obydwa kompilatory — Delphi i Kyliksa — ukierunkowane są na architekturę procesorów ´86, istnieje między nimi kilka istotnych różnic, których należy być świadomym podczas tworzenia przenośnych aplikacji międzyplatformowych.

Symbol kompilacji warunkowej LINUX

Obydwa wspomniane kompilatory predefiniują charakterystyczne dla siebie symbole kompilacji warunkowej: dla Kyliksa jest to symbol LINUX, dla Delphi — symbole MSWINDOWS i WIN32. Można więc łatwo wprowadzać do aplikacji elementy kodu źródłowego charakterystyczne wyłącznie dla jednego z tych środowisk:

 

{$IFDEF LINUX}

  …

  // kod specyficzny dla Linuksa

  …

{$ENDIF}

{$IFDEF MSWINDOWS}

  …

  // kod specyficzny dla Windows

  …

{$ENDIF}

 

Format PIC

Kompilator Kyliksa produkuje kod wynikowy w tzw. formacie PIC (Position Independent Code), różniący się pod wieloma względami od kodu w formacie PE (Portable Executable) produkowanego przez kompilatory windowsowe. Choć różnice te są bez znaczenia dla programisty ograniczającego się do „czystego” Pascala, stają się niezwykle istotne w przypadku używania języka asemblera — wbudowanego lub pod postacią dołączanych modułów zewnętrznych. Najbardziej charakterystyczną cechą formatu PIC jest adresowanie wszystkich danych globalnych względem rejestru EBX (jako rejestru bazowego), tak więc poprawna w środowisku Windows instrukcja

 

mov eax,SomeVar

 

w środowisku Linuksa powinna być zapisana jako

 

mov eax, [ebx].SomeVar

 

Ponadto zawartość samego rejestru EBX musi być chroniona przed zmianami przez wywoływane funkcje i procedury. W związku z tym kompilator Kyliksa predefiniuje kolejny symbol kompilacji warunkowej — PIC — dzięki czemu rozróżniać można kod przeznaczony dla poszczególnych platform:

 

{$IFDEF PIC}

  mov eax, [ebx].SomeVar

{$ELSE}

  mov eax,SomeVar

{$ENDIF}

 

Konwencje wywoływania

Charakterystyczne dla Windows konwencje stdcall i safecall nie istnieją w Kyliksie i mapowane są w jego środowisku na konwencję cdecl. Należy uwzględnić ten fakt przy ustaleniach dotyczących kolejności przekazywania parametrów i czyszczenia stosu wywołania.

Indywidualne cechy platformy systemowej

Jest oczywiste, iż w aplikacjach międzyplatformowych nie należy odwoływać się do mechanizmów typowych tylko dla konkretnej platformy — oto kilka z nich:

 

·         Linux nie stosuje literowych oznaczeń napędów dyskowych.

·         Separatorem katalogów w ścieżce jest w Windows odwrotny ukośnik (backslash — „\”), natomiast w Linuksie — prosty ukośnik (slash — „/”). Właściwą postać separatora można (w obydwu środowiskach) odczytać ze stałej PathSeparator.

·         W Windows poszczególne ścieżki na liście ścieżek rozdzielane są średnikiem, zaś w Linuksie — dwukropkiem.

·         Konwencja nazewnicza plików UNC (...

Zgłoś jeśli naruszono regulamin