Pakowanie w przestrzenie

2010-03-09 17:05

Jeśli w C++ piszemy jakąś bibliotekę (albo nawet ogólniej: kod wielorazowego użytku), to zgodnie z dobrymi praktykami powinniśmy jej symbole zamknąć przynajmniej w jedną osobną przestrzeń nazw (namespace). Dzięki temu zapobiegniemy zminimalizujemy możliwość kolizji identyfikatorów w kodzie, który z naszego dzieła będzie korzystał – a także potencjalnie z innych bibliotek.
Nie wszystkie z nich jednak mogą być tak ładnie napisane – choćby dlatego, że któraś może być przeznaczona oryginalnie dla języka C. Najbardziej typowy przykład? Windows API. Dołączenie windows.h zasypuje globalną przestrzeń nazw istną lawiną symboli odpowiadających tysiącom funkcji czy typów zadeklarowanych w tym nagłówku. Nie jest to specjalnie dobre.

Jak temu zaradzić? Bardzo prostą, ale nierozwiązującą wszystkich problemów metodą jest stworzenie własnego nagłówka “opakowującego” ten biblioteczny w nową przestrzeń nazw:

  1. #ifndef FOO_H
  2. #define FOO_H
  3.  
  4. namespace foo
  5. {
  6.     #include <foobar.h>
  7. }
  8.  
  9. #endif

Założenie jest takie, żeby wynikowego pliku nagłówkowego (foo.h) używać następnie w miejsce oryginalnego (foobar.h). Wtedy wszystkie symbole w nim zadeklarowane znajdą się wewnątrz nowej przestrzeni nazw, foo.

Wszystkie?… Nie! Pakując kod napisany w stylu C bezpośrednio do przestrzeni nazw nie osiągniemy bowiem wszystkich celów, którym namespace‘y przyświecają. Owszem, da się co nieco poprawić: jeśli np. wspomniany windows.h zamknęlibyśmy w przestrzeni win, to poniższy kod będzie jak najbardziej działał:

  1. win::HWND hNotepad;
  2. hNotepad = win::FindWindow("Notepad", 0);

podczas gdy wersja bez przedrostków win:: już niezupełnie. Jednak nie jest to całkowity – nomen omen – win, bo z kolei takie wywołanie:

  1. win::ShowWindow (hNotepad, win::SW_MINIMIZE);

skutkuje już niestety failem :) Nasza przestrzeń nie może bowiem zamknąć wszystkiego, gdyż nie podlegają jej dyrektywy preprocesora – a w szczególności #define. Pech polega na tym, że właśnie #define jest w C podstawowym sposobem definiowania stałych, więc użyta wyżej nazwa SW_MINIMIZE jest (w windows.h) określona po prostu jako:

  1. #define SW_MINIMIZE 6

Próba jej kwalifikowania powoduje zatem powstanie nieprawidłowego ciągu win::6 i słuszne narzekania kompilatora.

Nasz pojemnik (na nazwy) jest więc dziurawy i niestety nic z tym nie da się zrobić. Tak to już jest, gdy wciąż trzeba mieć do czynienia z API, które w tym przypadku liczy sobie – bagatelka – ponad 20 lat!

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


4 comments for post “Pakowanie w przestrzenie”.
  1. Malcom:
    March 9th, 2010 o 21:20

    Ja polecalbym pojsc dalej i nawet kod zwyklej aplikacji pakowac w unikalna przestrzen. Co procz zapobieganou potencjalnych kolizji, uchroni nas przed tworzeniem dlugich potworkow w nazwach typow i obiektow.

  2. revo:
    March 9th, 2010 o 22:33

    Takie pakowanie ma przeogromną wadę — jeżeli wczytasz jakiś nagłówek to preprocesor doda już sobie flagę guarda do zdefiniowanych rzeczy. Zatem kolejny include w globalnej przestrzeni nazw nic nie zrobi.

    Raz miałem styczność z tego typu problemem i był dość ciężki do wykrycia — ktoś przez przypadek w którymś z plików nagłówkowych zrobił include wewnątrz namespace. Przez to kilka plików cpp nie chciało się skompilować mimo tego, że widać było wszystkie wymagane include. Analizowanie pliku 7 MB (!) wyplutego przez preprocesor to żadna przyjemność. Ale takie już uroki wielkich projektów ;)

  3. Xion:
    March 10th, 2010 o 1:14

    @revo: Oczywiście tak – zakładam, że nagłówek “unamespace’owiony” zastępuje tutaj pierwotny we wszystkich miejscach w danym projekcie. Jeśli tak nie jest, to rzeczywiście różne cuda mogą się zdarzyć :)

  4. Syriusz:
    March 10th, 2010 o 20:26

    Ja tylko się zastanawiam, czy nawet jeśli udało by się, zapakować zawartość windows.h w namespace (lub inną bibliotekę bez wydzielonej przestrzeni nazw). To czy te wywołania były by poprawne? Znaczy się, czy nie burzył by się potem linker, tymi swoimi okrzykami unresolved external symbol… etc.

Comments are disabled.
 


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