POLITECHNIKA KRAKOWSKA - WIEiK KATEDRA AUTOMATYKI i TECHNIK INFORMACYJNYCH Metody Programowania www.pk.edu.pl/~zk/mp_hp.html Wykładowca: dr inż. Zbigniew Kokosiński zk@pk.edu.pl
Wykład 14: Przenośność oprogramowania Wprowadzenie Jak pisać przenośne oprogramowanie? Język Pliki nagłówkowe i biblioteki Organizacja programu Izolowanie Wymiana danych - kolejność bajtów Format CVF Przenośność i uaktualnianie Uniwersalne kodowanie Podsumowanie
Wprowadzenie Zapewnienie poprawności i wydajności programu pracującego w jednym środowisku wymaga od programisty wiele wysiłku. Czy stworzony program da się przenieść do środowiska z innym kompilatorem, procesorem lub systemem operacyjnym? Jeśli tak, to oprogramowanie cechuje wtedy przenośność (portability). Program jest tym bardziej przenośny im mniej modyfikacji niezbędnych do pracy w innym środowisku wymaga. Przenośność zwiększa zakres stosowalności danego programu. Oprogramowanie bardziej ogólne niż podano w pierwotnej specyfikacji będzie bardziej użyteczne i tańsze w przyszłości w utrzymaniu. Im mniej program zależy od specjalnych właściwości kompilatora, systemu operacyjnego czy sprzętu tym lepiej (jest bardziej odporny na nieunikniony upgrade). Program przenośny to równocześnie lepszy program, lepiej skonstruowany i lepiej przetestowany.
Wprowadzenie Zapewnienie poprawności i wydajności programu pracującego w jednym środowisku wymaga od programisty wiele wysiłku. Czy stworzony program da się przenieść do środowiska z innym kompilatorem, procesorem lub systemem operacyjnym? Jeśli tak, to oprogramowanie cechuje wtedy przenośność (portability). Program jest tym bardziej przenośny im mniej modyfikacji niezbędnych do pracy w innym środowisku wymaga. Przenośność zwiększa zakres stosowalności danego programu. Oprogramowanie bardziej ogólne niż podano w pierwotnej specyfikacji będzie bardziej użyteczne i tańsze w przyszłości w utrzymaniu. Im mniej program zależy od specjalnych właściwości kompilatora, systemu operacyjnego czy sprzętu tym lepiej (jest bardziej odporny na nieunikniony upgrade). Program przenośny to równocześnie lepszy program, lepiej skonstruowany i lepiej przetestowany.
Jak pisać przenośne oprogramowanie? Staramy się tak pisać oprogramowanie, aby mogło być wykonywane na zbiorze wspólnym różnych standardów, interfejsów i środowisk, w których ma potencjalnie pracować. Zamiast dodawać specjalny kod usuwający brak przenośności lepiej jest dostosować cale oprogramowanie do nowych ograniczeń (nowego zbioru wspólnego). Korzystając z abstrahowania i metod ukrywania specyficznych własności wyodrębniamy i kontrolujemy te fragmenty nieprzenośnego kodu, których nie udaje się uniknąć.
Język Rozwinięte języki programowania posiadają swoje standardy. Standard nie jest ścisłą definicją języka. Pozostawia pole do interpretacji twórcom kompilatorów, co z jednej strony powoduje mniej ograniczeń, ale także prowadzi do błędów i nieprzewidzianych problemów z przenośnością. Zasady ogólne: trzymaj się standardu programuj w głównym nurcie języka pamiętaj o niezdefiniowanych własnościach języka, prowadzących do większej elastyczności przy pisaniu kompilatorów, co jest niestety źródłem wielu problemów; Przykładowe problemy: rozmiary typów danych kolejność obliczeń uwzględnianie znaku liczby w wartościach typu char. przesunięcie arytmetyczne/logiczne kolejność bajtów wyrównywanie składowych struktury i klasy pola bitowe (zależne od maszyny)
Pliki nagłówkowe i biblioteki Wzbogacają język o dodatkowe usługi. Przykład: funkcje wejścia-wyjścia udostępniane przez pliki nagłówkowe stdio.h (język C), iostream.h (C++), java.io (Java). Usługi te nie należą do języka, ale są zdefiniowane razem z językiem i powinny być częścią środowiska, w którym są świadczone. Biblioteki są potencjalnym źródłem nieprzenośności. Zalecenie: używaj bibliotek standardowych (nieraz wbrew przyzwyczajeniom), np. funkcja strdup do kopiowania napisów jest poza ANSI C Problem z stdio.h, obsługującym kompilatory C sprzed ANSI C, ANSI C oraz C++. Stosowana wtedy kompilacja warunkowa z dyrektywami #if i #ifdef prowadzi do różnych komplikacji. Lepiej jest używać odrębne pliki nagłówkowe dla każdego kompilatora i środowiska.
Pliki nagłówkowe i biblioteki W niektórych wypadkach istnieją inne ograniczenia, będące źródłem problemów. Przykład: liczba sygnałów. W ANSI C jest 6, wychwytywanych funkcją signal. W standardzie POSIX jest 19. W większości systemów typu UNIX jest 32. Kompromis pomiędzy funkcjonalnością i przenośnością. Inne standardy: interfejsy systemów operacyjnych, sieciowe i graficzne. Są bardziej i mniej uniwersalne (POSIX, Win API). Zalecane jest postępowanie analogiczne jak wyżej.
Organizacja programu Są dwa biegunowe podejścia: suma albo iloczyn zbiorów właściwości. Wybór najlepszych cech systemów powoduje maksymalizację korzyści z możliwości każdego systemu ale płacimy za to rozmiarem programu i złożonością jego instalowania oraz komplikacją kodu zawierającego dyrektywy kompilacji warunkowej. Alternatywnie: wybór właściwości wszędzie dostępnych eliminuje wady poprzedniego podejścia za cenę ascezy, np. zmniejszenia liczby systemów docelowych czy wydajności systemu.
Izolowanie Błędem jest pisanie nieprzenośnego kodu rozproszonego po całym programie, postają problemy z kompilacją warunkową. Zalecenia: zależności systemowe powinny być zlokalizowane w oddzielnych plikach, po jednym dla każdego systemu. Pliki te mogą zawierać implementację przenośnego interfejsu do systemu operacyjnego można np. stworzyć przenośną bibliotekę graficzną dla edytora zamiast pisać interfejsy do takich systemów jak X Window. Taka biblioteka może posłużyć także innym programom. Przenośne środowiska graficzne takie jak OpenGL, Tcl/Tk i Java są obecnie dostępne dla różnych platform zależności od systemów ukrywaj poza interfejsami
Wymiana danych Do wymiany danych używaj tekstu!!!
Kolejność bajtów: big-endian oraz little-endian Gdy dane zapisywane są przy użyciu wielu (co najmniej dwóch) bajtów powstaje możliwość różnego uporządkowania tych bajtów w pamięci i podczas transmisji. Kolejność bajtów (ang. byte order, endianness) to konwencja ustalająca ten porządek. Big-endian (porządek malejący, od grubego końca ) to forma zapisu danych, w której jako pierwszy zapisywany jest najbardziej znaczący bajt. Dotyczy to np. procesorów SPARC, Motorola 68000, PowerPC 970 Występuje tu analogia z zapisem liczb uzywanym na co dzień. Little-endian (porządek rosnący, od cienkiego końca ) to forma zapisu danych, w której jako pierwszy zapisywany jest najmniej znaczący bajt. Dotyczy to np. procesorów x86, VAX Zmiana kolejności może być wykonana programowo lub sprzętowo. Robią to m.in. Procesory PowerPC (do serii G4), SPARC, ARM. Źródłosłów : J. Swift, Podróże Guliwera
Kolejność bajtów - przykład
Kolejność bajtów - przykład Wynik przedstawionego programu będzie różny w zależności maszyny. W maszynie PDP-11 (historycznej) występuje porządek mieszany z podziałem sekwencji rosnącej na dwubajtowe porcje a w nich porządek malejący : 11 22 33 44 22 11 44 33. Kolejność bajtów ma wielkie znaczenie przy transmisjach w połączeniach sieciowych.
Format CSV Format CSV (comma-separated values) jest popularnym sposobem przedstawiania danych stabelaryzowanych. Każdy wiersz tabeli jest jednym wierszem tekstu, a pola wiersza są odseparowane przecinkami. Format CSV nadaje się do zastosowania w arkuszach kalkulacyjnych, również tych zamieszczanych na stronach WWW. Wybierając odniesienie do strony Download Spreadsheet Format otrzymujemy potrzebne dane w formacie CSV. Dane tego typu można pobierać ze stron WWW automatycznie przy użyciu odpowiednich narzędzi, np. programu w języku Tcl. Do przetwarzania danych w formacie CSV potrzebna jest biblioteka, oraz operacje przekształceń numerycznych. W przypadku braku takiej biblioteki można ją zaprojektować.
Format CSV przykład 1
Format CSV przykład 2
Format CSV przykład 3 Program w języku Tcl, służący do połączenia się w witryną informacji giełdowych w sieci (połączenie z serwerem za pomocą tzw. sockets) i automatycznego pobrania danych w formacie CSV. Sekwencja f= występująca po znaku & informuje jakie wartości należy pobrać : s - nazwa akcji, 11 ostatnia cena, c1 zmiana w stosunku do ceny z dnia poprzedniego itp.). Ten przedstawiony format jest jednak nieudokumentowany.
Przenośność i uaktualnianie Ważnym źródłem nieprzenośności są zmiany w oprogramowaniu systemowym. Przykład 1: zmiana koncepcji (i interpretacji) polecenia echo w systemie Unix, powoduje nieprzenośność wielu skryptów powłoki zawierających to polecenie. Przykład 2: różne udoskonalenia w poleceniu sum obliczającym rozmiar i sumę kontrolną pliku w systemie Unix spowodowały, że wynik polecenia w zależności od wersji dają różne wyniki (!), co prowadzi do błędnych wniosków przy porównywaniu kopiowanych wersji plików z ich oryginałami. Rozwiązanie: przypisanie innej nazwy po zmianie specyfikacji. Przykład 3: zgodność programów z poprzednią wersją (backward compatibility) program (np.edytor tekstu, czy arkusz kalkulacyjny) powinien nie tylko czytać dane w formatach stosowanych w poprzednich wersjach, ale także zapisywać wyniki w tych formatach.
Uniwersalne kodowanie Anglocentryzm wielu twórców oprogramowania skutkuje nieprzenośnością oprogramowania ze względu na granice językowe i kulturowe. Nie można zakładać, ze znaki należą do 7-bitowego kodu ASCII. W Europie powszechnie stosuje się 8-bitowe rozszerzenia tego kodu, umożliwiające zapis różnych znaków diakrytycznych, różnych w rozmaitych językach. W Azji kodowanie znaków wymaga często 16 bitów (Chiny, Japonia, Korea). Uwaga na kolejność bajtów! Unicode, jako udana próba wprowadzenia jednego kodu na całym świecie (format popularny w Internecie, standardowy w Javie). Dokumenty w Unicode przekształca się ostatecznie w strumień 2-3 bajtów w kodzie UTF-8, a kod ASCII w jeden bajt UTF-8. Inne problemy: formaty dat, różna długość tekstów w różnych językach.
Literatura Kernighan B.W., Pike R. : Lekcja programowania, WN-T, Warszawa 2002 Kernighan B., Ritchie D. : Język ANSI C, WN-T, Warszawa 1988 Stroustrup B. : Język C++, WN-T, Warszawa 1997