Ostatnio w zobaczyłem ciekawy fragment kodu (C++):
#include "settings.hpp"
/* evil macro magic */
#undef _SETTINGS_HPP
#define extern
#include "settings.hpp"
#undef extern
Ciężko mi skomentować tak ciężkie nadużycie - które z drugiej strony jest genialne :-) Wyjaśnienie: powyższe linijki sprawiają, że wszystkie deklaracje "extern" zmiennych w "settings.hpp" stają się zwykłymi deklaracjami. Może to prowadzić do bardzo nieprzyjemnych błędów, ale zapewne czasami jest to użyteczne.
Subskrybuj:
Komentarze do posta (Atom)
Ktoś po prostu napotkał na taki problem, że jak dał definicje w pliku nagłówkowym, który był włączany w różnych jednostkach translacji to dostawał błędy od linkera (w końcu każda zmienna ma wówczas kilka kopii).
OdpowiedzUsuńTo nie jest genialne pod żadnym względem. Ewidentnie jest to napisane z braku wiedzy.
Jest to rozwiązanie nieskalowalne - nie można nawet mieć ustawień typu bez konstruktora domyślnego.
Jest to w C++, więc należałoby zrobić klasę ustawień i ewentualnie dać ją jako singleton.
Pomijając jednak to trywialne i narzucające się rozwiązanie można również w pliku nagłówkowym dać wszystkie definicje jako const. W końcu są to ustawienia, więc nie będą modyfikowane w czasie działania (tak, to wystarczy aby linker się nie rzucał).
Jeżeli to nie wystarcza (i piszemy w C) to należy zrobić plik settings.cpp w którym są wszystkie definicje zmiennych (i są statyczne dla tego modułu). Z zewnątrz odwołujemy się do ustawień poprzez funkcje, które są exportowane przez settings.h.
Pomijając fakt, że ustawienia są po to aby były wczytywane z pliku ;-P
Nie zgadzam się z Twoją interpetacją: zauważ, że settings.hpp wyraźnie *MA* zabezpieczenie przed wielokrotnym includowaniem. A osoba która to pisała wie jak działa linker ;-) To co tutaj mamy to unikanie deklarowania zmiennych dwa razy: dzięki temu mamy tylko jedno miejsce gdzie musimy zmieniać typ zmiennej, dajmy na to. Oczywiście jest tu mowa o zmiennych globalnych, co do których można mieć wiadome zastrzeżenia, ale w pewnych przypadkach ich istnienie jest zrozumiałe - wręcz porządane.
OdpowiedzUsuń>>> Pomijając jednak to trywialne i narzucające się rozwiązanie można również w pliku nagłówkowym dać wszystkie definicje jako const. W końcu są to ustawienia, więc nie będą modyfikowane w czasie działania (tak, to wystarczy aby linker się nie rzucał).
To absolutnie nie jest prawda. Co innego magiczne stałe zaszyte w programie, a co innego stan programu.
Doskonale rozumiem ten kod. Mam poważne zastrzeżenia do samej koncepcji tego "czegoś". Nie chodzi o zmienne globalne.
OdpowiedzUsuń> zauważ, że settings.hpp wyraźnie *MA* zabezpieczenie
> przed wielokrotnym includowaniem.
Właśnie o tym braku wiedzy pisałem. Trzeba jeszcze wiedzieć jak takie zabezpieczenie działa. Daj do pliku settings.hpp dyrektywę #warning WLACZANIE KODU.
To wypisze sie tyle razy ile plik bedzie wlaczany. Na kazda jednostke translacji raz. Jezeli dasz pliki a.cpp i b.cpp, które włączają settings.hpp i skompilujesz g++ a.cpp b.cpp to niezależnie czy masz zabezpieczenie czy nie, włączysz kod dwa razy (warning się pokaże dwa razy). Dlatego linker będzie szalał gdy w pliku settings.h będzie *definicja* zmiennej, która nie jest stałą. Tak samo z funkcją czy metodą, która nie jest "inline".
> Co innego magiczne stałe zaszyte w programie, a co
> innego stan programu.
Myślałem, że mówimy o ustawieniach programu (czyli grupie "magicznych stałych"). Warto dla nich zrobić osobną klasę (lub moduł w C, do którego dostęp jest przez funkcje) choćby po to aby w przyszłości dało się zmienić sposób pamiętania ustawień (np. wywalić to do pliku konfiguracyjnego).