Główne pytanie brzmi: czy chcesz, aby plik konfiguracyjny był w jakimś kompletnym języku Turinga (takim jak Python)? Jeśli tego chcesz, możesz również rozważyć osadzenie innego języka skryptowego (kompletnego Turinga), takiego jak Guile lub Lua (ponieważ może być postrzegany jako „prostszy” w użyciu lub osadzaniu niż Python; przeczytaj rozdział Rozszerzanie i Osadzanie Pythona ). Nie będę o tym dalej dyskutować (ponieważ inne odpowiedzi - na przykład Amon - omawiał to dogłębnie), ale zauważam, że osadzenie języka skryptowego w twojej aplikacji jest ważnym wyborem architektonicznym , który powinieneś rozważyć bardzo wcześnie; Naprawdę nie polecam dokonywania tego wyboru później!
Dobrze znanym przykładem programu konfigurowalnego przez „skrypty” jest edytor GNU emacs (lub prawdopodobnie AutoCAD w sferze zastrzeżonej); więc miej świadomość, że jeśli zaakceptujesz skrypty, jakiś użytkownik w końcu skorzysta - i być może nadużywanie, z twojego punktu widzenia - z tego narzędzia i stworzy skrypt wielotysięczny; dlatego wybór wystarczająco dobrego języka skryptowego jest ważny.
Jednak (przynajmniej w systemach POSIX), możesz uznać za wygodne włączenie dynamicznego obliczania „pliku” konfiguracji w czasie inicjalizacji (oczywiście pozostawiając ciężar rozsądnej konfiguracji administratorowi systemu lub użytkownikowi; w rzeczywistości jest to konfiguracja tekst pochodzący z jakiegoś pliku lub polecenia). W tym celu możesz po prostu przyjąć konwencję (i udokumentować ją), że ścieżka pliku konfiguracyjnego zaczynająca się od np. A !
lub a |
jest w rzeczywistości poleceniem powłoki , które czytałbyś jako potok . To pozostawia użytkownikowi możliwość wyboru dowolnego „preprocesora” lub „języka skryptowego”, który jest mu najbardziej znany.
(jeśli użytkownik akceptuje konfigurację obliczaną dynamicznie, musi zaufać użytkownikowi w kwestiach bezpieczeństwa)
Zatem w kodzie inicjalizacyjnym main
(na przykład) zaakceptujesz jakiś --config
argument confarg
i wyciągniesz FILE*configf;
z niego trochę . Jeśli ten argument zaczyna się od !
(tzn. Jeśli (confarg[0]=='!')
....), użyjesz configf = popen(confarg+1, "r");
i zamkniesz ten potok za pomocą pclose(configf);
. W przeciwnym razie użyjesz configf=fopen(confarg, "r");
i zamkniesz ten plik za pomocą fclose(configf);
(nie zapomnij o sprawdzaniu błędów). Patrz rura (7) , popen (3) , fopen (3) . Aby zapoznać się z aplikacją zakodowaną w języku Python, przeczytaj o os.popen itp.
(dokument również dla dziwnego użytkownika, który chce przekazać plik konfiguracyjny o nazwie !foo.config
pass, ./!foo.config
aby ominąć popen
powyższą sztuczkę)
Przy okazji, taka sztuczka jest jedynie wygodą (aby uniknąć wymagania od zaawansowanego użytkownika np. Kodowania jakiegoś skryptu powłoki w celu wygenerowania pliku konfiguracyjnego ). Jeśli użytkownik chce zgłosić błąd, powinien wysłać wygenerowany plik konfiguracyjny ...
Zauważ, że możesz również zaprojektować swoją aplikację z możliwością używania i ładowania wtyczek w czasie inicjalizacji, np. Za pomocą dlopen (3) (i musisz zaufać swojemu użytkownikowi w kwestii tej wtyczki). Ponownie, jest to bardzo ważna decyzja architektoniczna (i musisz zdefiniować i dostarczyć trochę raczej stabilnego API i konwencji dotyczących tych wtyczek i twojej aplikacji).
W przypadku aplikacji napisanej w języku skryptowym, takim jak Python, można również zaakceptować argument programu dla eval lub exec lub podobnych prymitywów. Ponownie, problemy związane z bezpieczeństwem są wówczas przedmiotem zainteresowania (zaawansowanego) użytkownika.
Jeśli chodzi o format tekstowy pliku konfiguracyjnego ( niezależnie od tego, czy jest generowany, czy nie), uważam, że najczęściej musisz go dobrze udokumentować (a wybór określonego formatu nie jest tak ważny; zalecam jednak, aby użytkownik mógł niektóre -przesłane-komentarze w nim). Możesz użyć JSON (najlepiej z niektórym parserem JSON, który akceptuje i pomija komentarze ze zwykłymi //
do eol lub /*
... */
...), YAML, XML, INI lub własną rzeczą. Analiza pliku konfiguracyjnego jest dość łatwa (a znajdziesz wiele bibliotek związanych z tym zadaniem).