Ze względu na to, że jestem w trakcie tworzenia biblioteki która ma zamiar w przyszłości ujrzeć światło dzienne i powiedzieć “hello, world” na ekranie [mam nadzieję] wielu programistów i użytkowników końcowych, pomyślałem o tym, żeby w końcu zająć się napisaniem stosownej dokumentacji do projektu. Przez długi czas w moim kodzie funkcjonowały przeróżne komentarze opisujące mniej lub bardziej dokładnie “co się w danym miejscu dzieje”, ale były to bardziej opisy dla mnie, jako twórcy rozumiejącego cel i działanie poszczególnych elementów, niż dla kogoś, kto miałby w przyszłości korzystać z tego produktu. Pomyślałem więc [w “międzyczasie” zmuszając się do odrzucenia standardowego podejścia w stylu “dlaczego miałbym korzystać z rozwiązań zewnętrznych, skoro sam mogę stworzyć generator dopasowany do moich wymagań?”], że należałoby znaleźć dobre narzędzie pozwalające na stworzenie dokumentacji na podstawie kodu źródłowego w łatwy i w miarę przyjazny sposób.

Wybór padł na Doxygen, ze względu na wcześniejszą styczność z tym systemem oraz fakt, że jako jeden z nielicznych potrafi wygenerować z jednego źródła całkiem pokaźną liczbę formatów wyjściowych, od zwykłego zestawu plików HTML, przez PDF i LaTeX, aż do Microsoftowego formatu Compiled HTML Help [CHM]. Format CHM zaznaczyłem dlatego, że uważam go za bardzo udany produkt, moim zdaniem naprawdę bardzo wygodnie korzysta się z tego typu materiałów. Oprócz tego wspiera różne języki [dla mnie najważniejsza jest obsługa C++ i PHP] i formaty komentarzy dokumentujących, takich jak JavaDoc, czy QT, tak więc przydaje się w wielu sytuacjach i w różnych środowiskach.

Na blogu zajmuję się rozwiązywaniem problemów z różnymi rzeczami, tak więc ten wpis także nie jest poświęcony samemu Doxygenowi - jak zaznaczyłem w temacie, wiele osób korzystających z niego ma problem z tzw. “include guards”. Include guard to technika pozwalająca na upewnienie się, że zgodnie z zasadą “One Definition Rule” [nie tylko] języka C++, każdy plik zawierający definicję jakiegokolwiek elementu będzie przetwarzany tylko raz, tak, aby nie powodować błędu powtórzenia tego samego kodu. Umieszczenie rzeczonych “strażników” korzystając ze składni:

#ifndef GUARD
#define GUARD

// ...
// tysiące linii ;]
// ...

#endif

przy włączonym przetwarzaniu makr preprocesora [fragment domyślnego pliku wygenerowanego poleceniem doxygen -g dla Doxygena 1.6.1]:

# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.

ENABLE_PREPROCESSING   = YES

co, jak widać, jest domyślną wartością rzeczonego parametru, powoduje pominięcie całego bloku nimi objętego, przez co przy przetwarzaniu źródeł przez analizator Doxygena dostajemy błąd o treści:

Warning: documented function [nazwa elementu] was not declared or defined.

ze względu na fakt, że [przynajmniej w C++] zwykle są objęte są nimi deklaracje, a nie definicje, przez co analizator widzi implementację elementu w pliku .cpp, a nie widzi skąd on [deklaracja] pochodzi.

Istnieją dwa rozwiązania dla tego problemu:

  • zamiana include guards z wyżej opisanej składni #ifdef na deklarację:

#pragma once

na początku każdego “strzeżonego” pliku.

  • wyłączenie przetwarzania makr preprocesora, czyli ustawienie opcji ENABLE_PREPROCESSING na NO.
Należy jednak należy pamiętać, że w pierwszym przypadku nie każdy kompilator obsługuje parametr “once”, a w drugim pozbawiamy się możliwości, które daje nam ustawienie MACRO_EXPANSION, chociaż jaki porządny programista używa preprocesora w takim zakresie, żeby to dokumentować. ;] Ja także stwierdziłem, że wolę pozbawić się rozwijania makr, zamiast przestawiać się na dyrektywę #pragma, tak więc już mogę się cieszyć poprawnie wygenerowaną dokumentacją. ;]

W internecie niestety nie istnieje wiele informacji poświęconych temu problemowi, dlatego zdecydowałem się o tym napisać, mam nadzieję, że pomoże to Wam korzystać z genialnego narzędzia, jakim jest Doxygen.

Linki: [0] http://www.doxygen.org/ - Oficjalna strona produktu.