wtorek, 20 października 2009

Logger

Miała miejsce długa przerwa, ale nie jest źle. Ostatecznie to będzie drugi wpis w tym miesiącu ;)

Oddałem pracę i dostałem lenia. Kompletnie nic mi się nie chciało. Chyba musiałem trochę odreagować. Powoli jednak zbieram się do roboty, choć nieco straszy mnie obrona. Kochana uczelnia raczyła mnie postawić w bardzo nieprzyjemnej sytuacji. Termin egzaminu ma być za tydzień, czyli 26 - 27 X, ale na dzień dzisiejszy nikt nie wie kiedy dokładnie. Co za tym idzie nie jest znany skład komisji. Boję się, że termin wypadnie na poniedziałek, a poinformują mnie w piątek, oczywiście po południu. I jak ja niby mam się przygotować ?

Od dawna zastanawiałem się nad poprawieniem loggera. To co stworzyłem na początku projektu było skuteczne, poza tym miało same wady. Rozwiązanie to jest oparte o wzorzec singletona, do tego bardzo zależało mi na łatwym formatowaniu a'la strumienie (operator <<) oraz na śledzeniu przebiegu wywołań funkcji. Wyszło rozwiązanie paskudne w użyciu i co znacznie gorsze, nie spełniające do końca moich oczekiwań.
Problemem okazało się zbieranie informacji o stosie. Automatyczna zamiana adresu funkcji na jej nazwę nie jest zadaniem trywialnym. Coś takiego można znaleźć na blogu Gynvaela Coldwinda: w tym wpisie. Sam wymyśliłem nieco inne rozwiązanie, również oparte o makra. W skrócie mniej asemblera, więcej C++. Tylko czy ja mam ochotę przejrzeć 37 linii kodu i pozamieniać definicje wszystkich funkcji w makra ? Wymyśliłem więc, że trzeba ręcznie opatrzyć każdy zapis do logu informacją o tym skąd pochodzi. Sprawa jest dosyć prosta, posłużyłem się standardowym makrem __FUNCTION__. Nieco kłopotliwa jest nazwa klasy w przypadku metod. Rozwiązałem to tak:
#define __CLASS__ "Nazwa klasy"
#define HERE __CLASS__ << "::" << __FUNCTION__

No i działa. Brakuje jednak danych stosu, czyli informacji o stanie aplikacji gdy doszło do powstania wpisu. Fajnie by też było mieć zarejestrowane trochę danych o tym co działo się wcześniej.

Zadałem sobie jednak fundamentalne pytanie, czy umieszczanie nazw funkcji i klas w kodzie ma sens ? No i wyszło mi, że nie. Wracamy do punktu wyjścia, jak zamienić adres funkcji na nazwę ? Linker z Visual Studio ma taki fajny przełącznik /MAP. W efekcie powstaje plik tekstowy chyba ze wszystkim co nam do szczęścia potrzebne, tj adres w pliku, adres w obrazie, nazwa funkcji, atrybuty, plik z którego dana funkcja pochodzi.

Na koniec zabawny błąd:
Logger LoggerInstance;
std::fstream logFile;
Logger::Logger()
{

logFile.open("log.txt", std::fstream::out);
...
}

Otwarcie pliku kończy się wyjątkiem przy próbie odczytu spod adresu 0. Czemu? Bo w momencie wywołania tego konstruktora obiekt logFile jeszcze nie powstał ;) Trzeba zamienić kolejność.
std::fstream logFile;
Logger LoggerInstance;

2 komentarze:

Reg pisze...

Kolejność w jakiej inicjalizowane są zmienne globalne jest niezdefiniowana, czyli może być jakakolwiek i zmieniać się z kompilacji na kompilację - a przynajmniej po jakiś zmianach w programie albo zmianie kompilatora. Dlatego nie można na niej polegać.

Adam Śmigielski pisze...

Hm, a możesz podać jakieś źródło ?

Mi się wydawało, że obiekty globalne zdefiniowane w jednej jednostce translacji są inicjalizowane zgodnie z kolejnością definicji.

Natomiast w przypadku dwóch różnych jednostek kolejność jest nie zdefiniowana.