Bolączki C++ #2 – Pakietowanie kodu

2007-09-01 19:05

W czasach panowania języków proceduralnych i strukturalnych – jak C czy Pascal – możliwości ukrywania i ochrony kodu przed niepowołaną zmianą były, delikatnie mówiąc, słabe. Wszystkie funkcje, pola i struktury były dostępne na zewnątrz i tylko dzięki programistycznej kurtuazji (i czytaniu dokumentacji technicznej, o ile istniała) zawdzięczało się poprawne działanie kodu.
Sytuacja poprawiła się wraz z językami obiektowymi, takimi jak C++. Mamy już klasy, których składniki mogą być określone jako publiczne, chronione (protected) albo prywatne. Jeżeli tylko odpowiednio oznaczymy składowe należące do implementacji, nikt niepowołany nie będzie miał do nich dostępu zwyczajnymi środkami.
Od tego czasu świat poszedł jednak do przodu pod względem technik organizacji kodu. Nowsze języki programowania – jak Java, C# czy Python – posiadają wbudowane mechanizmy pakietów (zwanych w .NET złożeniami – assemblies). Dzięki nim można grupować klasy wykonujące wspólne zadania i zapewniać dostęp do wybranych składowych wszystkich klasom z danego pakietu. W precyzyjnym określaniu widoczności celuje zwłaszcza C#.

W C++ teoretycznie mamy dość podobne możliwości, oferowane przez kilka osobnych mechanizmów językowych. Czymś co zdaje się najbardziej przypominać pakiety lub złożenia są przestrzenie nazw (namespaces). Ich przeznaczeniem jest przede wszystkich jednak zabezpieczenie przed dublowaniem i niejednoznacznością nazw. Na przykład klasa string z STL jest zawarta w przestrzeni std i dzięki temu nie będzie pomylona z jakąś inną klasą łańcuchów znakowych o tej samej nazwie.
Jednak przestrzenie nazw to bardzo ułomny sposób pakietowania. Elementy jednej przestrzeni (klasy, funkcje) domyślnie są dla siebie tak samo obce, jak te należące do różnych przestrzeni. Można to oczywiście zmienić, korzystając z deklaracji przyjaźni; wymaga to jednak znajomości wszystkich tych “przyjaciół” – czyli odpowiednich deklaracji zapowiadających – co jest bardzo niewygodne. Naturalnie można powiedzieć, że wtedy przynajmniej wiemy, kto nas “podgląda”. Lecz co z tego, skoro w takim zaprzyjaźnianiu stosować można wyłącznie zasadę wszystko albo nic: klasa/funkcja będzie widziała albo wszystko składowe, albo tylko publiczne (domyślnie). Bez pokrętnych sztuczek nie da się udostępnić pewnych składników tylko w ramach “pakietu”.

Całkiem osobną kwestią jest też to, jak mechanizm przestrzeni nazw współpracuje z tą zakałą C++, czyli plikami nagłówkowymi i dyrektywą #include. W nowszym językach wystarcza instrukcja podobnej do tej:

  1. using System.Windows.Forms;

żeby jednocześnie zadeklarować użycie pakietu i uchronić się przed koniecznością używania kwalifikowanych nazw (w tym przypadku np. ListBox zamiast System.Windows.Forms.ListBox). W C++ potrzebujemy do tego dwóch dyrektyw:

  1. #include <vector>
  2. using namespace std;

i niby wszystko byłoby w porządku. Problem w tym, że wielce inteligentny mechanizm plików nagłówkowych oraz #include sprawia, że nazw niekwalifikowanych możemy używać praktycznie tylko w plikach *.cpp. Gdyby bowiem umieścić using namespace w nagłówku, to instrukcja ta zostałaby bezmyślnie powielona we wszystkich plikach, które ten nagłówek dołączają. A wtedy przestrzeń nazw nie spełniałaby nawet tej najprymitywniejszej funkcji, czyli ochrony przed wieloznacznością powtarzających się nazw.

Tak naprawdę to same przestrzenie nazw nie są złe. Nawet same pliki nagłówkowe z C++ nie byłyby aż tak bardzo złe, gdyby ich zadaniem nie było tylko ułatwianie życia kompilatorowi. Dopiero połączenie tych dwóch rzeczy sprawia, że zdecydowanie odechciewa się używać którejkolwiek z nich ;P
Istnieje jednak pewien optymistyczny akcent. Jeśli kiedykolwiek zostanie zniesiony w C++ podział na dwa rodzaje plików z kodem, wówczas i przestrzenie nazw będą musiały się przekształcić w coś podobnego do pakietów Javy czy złożeń z .NET. Podobnie jest w drugą stronę: jeżeli C++ dorobi się mechanizmu pakietów, wtedy pliki nagłówkowe i dyrektywa #include nie będą miały wielkiego sensu. Wypada więc tylko kibicować Komitetowi Standaryzacyjnemu w wykonaniu tego pierwszego kroku, bo drugi prawdopodobnie zrobią siłą rozpędu :)

Tags: ,
Author: Xion, posted under Programming »


3 comments for post “Bolączki C++ #2 – Pakietowanie kodu”.
  1. SirMike:
    September 2nd, 2007 o 11:41

    A ja nie chcialbym takich zmian. Dobrze mi z tym co jest i mam poczucie wladzy nad moim kodem. Wszechobecna hermetyzacja dobra jest w jezykach stricte obiektowych a C++ do takich nie nalezy. Trzeba by zmienic naprawde sporo w samym standardzie i obawiam sie, ze Stroustrup na to nie pojdzie :P Nie mozna ot tak zmienic standardu w sposob, ktory rozpieprzy kod ludziom na calym swiecie.

  2. Xion:
    September 2nd, 2007 o 20:50

    To nieustanne oglądanie się w przeszłość sprawia też, że za językiem ciągną się zaszłości jeszcze z czasów C (jak np. char* cos = “costam”). Twórcy innych języków jakoś nie mają zbyt wielu oporów, by wprowadzać niekiedy niekompatybilne rozwiązania czy zaprzestawać wspierania starszych wersji. Ostatnio na przykład PHP w wersji 4 ostatecznie przestał być wspierany.
    Być może najlepszym wyjściem byłby branching ‘zmodułowanej’ i zwyczajnej wersji języka, z wsparciem i standardami dla obu. Tylko czy gałąź podzielona szybciej nie uschnie…

  3. SirMike:
    September 2nd, 2007 o 22:23

    char *cos = “costam” to tylko wskaznik na napis, chyba nikt “zdrowy”, piszacy w c++ nie bedzie korzystal z takich cudakow.

    A PHP to zupelnie inna bajka, juz dawno przestalem w tym pisac bo z wersji na wersje mamy wszystko “do gory nogami”. Wole porzadniejsze rozwiazania w stylu ASP.NET

Comments are disabled.
 


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