libgreattao i trzy domeny konfiguracji
W tym wpisie chciałbym opisać o tym, jak przekazywać zmienne(konfigurację) do aplikacji i rdzenia libgreattao. Pisząc o rdzeniu mam na myśli główną bibliotekę, bez backendów. Konfigurację rdzenia należy podzielić na konfigurację powłoki i klas okien.
Zaczniemy od tego, jak wywoływać aplikację, by podać konfigurację aplikacji, powłoki i klas okien.
Konfiguracja może pochodzić z plików lub linii wywołania. Konfiguracja aplikacji dodatkowo może być odczytana z parametrów po trzech dodatkowych dekoratorach, a dodatkowo z okna konfiguracji(okno to jest generowane automatycznie przez libgreattao). W przyszłości dojdzie jeszcze konfiguracja pochodząca z sieci. Zamierzam do libgreattao dodać serwer sieciowy, a także napisać klienta. Oczywiście, że źródło sieć będzie najmniej ważne, by nie wprowadzać niepotrzebnego zagrożenia dla aplikacji.
Domena aplikacji
Na początku zajmiemy się konfiguracją aplikacji. Pierwszym opisanym sposobem wywołania będzie odczyt konfiguracji z pliku.
Konfiguracja z pliku
./nasz_program --tao-app-config-from-file /ścieżka/do/pierwszego/pliku,/ścieżka/do/drugiego/pliku
Libgreattao wczyta konfigurację z plików w podanej kolejności, a następnie wczytywane pliki będą przesłaniać konfigurację poprzednich plików. Działają takie same zasady, jak w przypadku plików z konfiguracją użytkownika dla klas okien dla poszczególnych aplikacji(~/.tao/apps), czyli poszczególne opcje składają się z nazwy i wartości, oddzielonych znakiem równości. Przy czym dopuszczone jest stosowanie wielu słów dla nazwy/wartości, a dodatkowo można stosować symbole ucieczki, jak '\ ', '\n', '\='.
Konfiguracja z wiersza poleceń - line
./nasz_program --tao-app-config-from-line pierwsza_zmienna=pierwsza_wartość,druga_zmienna=druga_wartość
Parametry konfiguracji są oddzielone przecinkiem. Każdy parametr składa się ze zmiennej(nazwy zmiennej) i wartości.
Konfiguracja z wiersza poleceń - tao‑switch
./nasz_program --tao-app-config-tao-switch-pierwsza_opcja pierwsza_wartość --tao-app-config-tao-switch-druga_opcja druga_wartość
W tym wypadku podaje się dla każdej opcji -‑tao-app-config=tao-switch- , po czym należy podać nazwę opcji, a następnie(jako następny argument) wartość opcji.
Długa opcja - odpowiednik składni getopts
./nasz_program --pierwsza_opcja pierwsza_wartość --druga_opcja druga_wartość
Tutaj magi nie ma. Jest to odpowiednik długiej opcji getopts. Podaje się tutaj dwa myślniki, a następnie nazwę opcji. Kolejnym argumentem musi być wartość opcji. Różnica pomiędzy poprzednimi sposobami, a tym jest taka, że w przypadku poprzednich sposobów przełącznik, jak i kolejny argument są usuwane z listy argumentów. Tutaj pozostają nietknięte. Jest to zamierzone, bo wygląda to tak, jak normalne przekazywanie opcji w systemie.
Znak-przełącznik - (nie) odpowiednik składni getopts
./nasz_program -a pierwsza_wartość -b druga_wartość
Tutaj sprawa wygląda, jak powyżej. Przełącznik, jak i występujący po nim argument pozostają nietknięte. Tutaj, jak w przypadku krótkich argumentów getopts, podaje się znak oznaczający argument. Jednak tych znaków nie można sklejać i po każdym takim znaku musi wystąpić argument z wartością.
Kolejność odczytywania
Na samym początku są wczytywane pliki. Jak już wyjaśniono są one odczytywane w kolejności takiej, jaką podano - czyli wartości w późniejszych plikach przesłaniają to, co było we wcześniejszych plikach. Potem jest odczytywana linia poleceń. Dla każdego parametru można jednak określić, jakie dekoratory są akceptowane. I tak polecenie może pozwolić na używanie tao‑przełączników, długich przełączników, bądź krótkich przełączników. Linia poleceń tao(tam, gdzie się podaje w linii poleceń opcje po przecinku) jest zawsze włączona dla źródła linii poleceń. I tak, jeśli aplikacja chce czytać z linii poleceń, to kolejność odczytywania jest następująca:
- Opcje tao-przełącznik
- Długie dekoratory
- Znaki dekoratory
- Linia tao
Oczywiście dla każdej opcji dany dekorator będzie brany pod uwagę, jeżeli dla tej opcji została użyta dana flaga. Linia tao jest zawsze brana pod uwagę. Dekoratory z ich wartościami odczytywanymi później przesłaniają dekoratory odczytane wcześniej.
Odpytywanie o argumenty
[code=C] #include <libgreattao/config.h> #include <libgreattao/tao.h">
const struct tao_app_config_option options[4] = { {"a", "a", "a", TAO_TYPE_STRING, 'a', TAO_CONFIG_OPTION_ARG_TAO_DECORATOR | TAO_CONFIG_OPTION_ARG_LONG_DECORATOR | TAO_CONFIG_OPTION_ARG_CHARACTER_DECORATOR}, {"b", "b", "b", TAO_TYPE_INT, 'b', TAO_CONFIG_OPTION_ARG_TAO_DECORATOR | TAO_CONFIG_OPTION_ARG_LONG_DECORATOR | TAO_CONFIG_OPTION_ARG_CHARACTER_DECORATOR}, {"c", "c", "c", TAO_TYPE_UINT, 'c', TAO_CONFIG_OPTION_ARG_TAO_DECORATOR | TAO_CONFIG_OPTION_ARG_LONG_DECORATOR | TAO_CONFIG_OPTION_ARG_CHARACTER_DECORATOR}, {"file", "file", "file", TAO_TYPE_FILE_PATH, 0, 0} };
struct tao_app_config_result results[4];
int main(int argc, char **argv) { tao_initialize("tao options demonstration", "", &argc, argv); tao_get_app_configuration(options, results, 4, TAO_APP_CONFIG_FROM_LINE | TAO_APP_CONFIG_FROM_FILE) ; if (results[0].error) { printf("Error getting string\n"); } if (results[1].error) { printf("Error getting integer\n"); } if (results[2].error) { printf("Error getting unsigned\n"); } if (results[3].error) { printf("Error getting file path\n"); } printf("\"%s\" %d %d \"%s\"\n",results[0].result,results[1].result,results[2].result,results[3].result); } [/code]
Tak wygląda krótki program, który pobiera opcje, a następnie wyświetla, które opcje zostały nieprawidłowo wczytane, po czym wyświetla wszystkie opcje. Najważniejsza jest funkcja tao_get_app_configuration. Przyjmuje jako pierwszy argument wskaźnik do struktur opisu opcji, który nie zostanie zmodyfikowany przez funkcję. Następny jest wskaźnik do struktur rezultatu, który będzie modyfikowany - wymagane jest jednak, by program sam wyzerował te struktury. W naszym przypadku te struktury będą wyzerowane, bo są alokowane w specjalnym miejscu. Kolejnym argumentem jest ilość takich struktur(ilość struktur opisu opcji, jak i struktur dla rezultatów musi się zgadzać), po czym występują flagi. Flagami mogą być:
- TAO_APP_CONFIG_FROM_FILE
- TAO_APP_CONFIG_FROM_LINE
- TAO_APP_CONFIG_INTERACTIVE
- TAO_APP_CONFIG_INTERACTIVE_FORCE
Pierwsza flaga powoduje odczytanie konfiguracji z plików. By jednak nie powodować problemów, to konfiguracja jest wczytywana w momencie uruchomienia programu. Kolejna flaga powoduje odczyt konfiguracji z linii poleceń. Ostatnie dwie flagi powodują wyświetlenia okna konfiguracji. Różnica pomiędzy nimi polega na tym, że flaga z sufiksem _FORCE spowoduje wyświetlenie wszystkich opcji - również tych wypełnionych. Dodatkową różnicą jest to, że opcja bez tego sufiksu nie wyświetli okna konfiguracji w przypadku, gdy wszystkie opcje otrzymały wartość.
Teraz powinnyśmy się zająć strukturą opisu opcji. Pierwszą składową takiej struktury jest nazwa opcji. Będzie wykorzystywana do uzupełniania końcówek przełączników w linii poleceń, a także do przeszukiwania plików konfiguracji. Drugą składową jest widoczna dla użytkownika nazwa. Będzie wykorzystywana do okien konfiguracji,. Trzecią składową jest opis opcji. Będzie również wykorzystywana do okien konfiguracji. Czwartą składową jest typ opcji. Wykorzystano wszystkie dopuszczone wartości, czyli dla ciągu znaków, liczby naturalnej, całkowitej, a także ścieżki do plików. Ścieżka do pliku jest traktowana tak samo, jak ciąg znaków, poza oknami konfiguracjami, które to dla takich typów pozwalają na wybranie pliku z okna otwarcia pliku. Kolejnym argumentem jest znak, który będzie wykorzystywany w przypadku krótkich przełączników(znaków). Ostatnim argumentem są flagi, które w przypadku odczytu z linii poleceń, pozwalają na określenie typów dekoratorów.
Przedstawiony program nie wykorzystuje okien konfiguracji. Aby to zmienić wystarczy dopisać odpowiednią flagę do ostatniego argumentu opisywanej wyżej funkcji. Jednak implikuje to to, że funkcja nigdy nie wróci z błędami w danych, bo okno konfiguracji pilnuje, by wszystkie opcje były odpowiednio wypełnione. Jedynie w przypadku, gdy użytkownik zamknie okno konfiguracji, funkcja przypisze wszystkim opcją błąd.
Konfiguracja shella
./nasz_program --tao-shell-config-from-file /ścieżka/do/pliku/konfiguracji/1,/ścieżka/do/pliku/konfiguracji/2
Jak w przypadku konfiguracji aplikacji. Pliki są wczytywane w kolejności podania i mają taką samą składnie, co w przypadku plików konfiguracji aplikacji. Drugi przypadek
./nasz_program --tao-shell-config-from-line pierwsza_opcja=pierwsza_wartość,druga_opcja=druga_wartość
Tutaj również, jak w przypadku konfiguracji aplikacji.
Oczywiście, opcje podane w linii poleceń są ważniejsze od tych odczytanych z plików.
Teraz muszę pokazać, jak te dane wykorzystać.
=fromconfig nazwa_zmiennej nazwa_opcji_konfiguracji
Polecenie to zapisuje do zmiennej o podanej nazwie wartość opcji konfiguracji o podanej nazwie.
Konfiguracja klas okien
./nasz_program --tao-design-config-from-file /ścieżka/do/pliku/konfiguracji/1,/ścieżka/do/pliku/konfiguracji/2
Jak w przypadku konfiguracji aplikacji. Pliki są wczytywane w kolejności podania i mają taką samą składnie, co w przypadku plików konfiguracji aplikacji. Drugi przypadek
./nasz_program --tao-design-config-from-line pierwsza_opcja=pierwsza_wartość,druga_opcja=druga_wartość
Tutaj również, jak w przypadku konfiguracji aplikacji.
Oczywiście, opcje podane w linii poleceń są ważniejsze od tych odczytanych z plików.
Teraz muszę pokazać, jak te dane wykorzystać.
<root> <fromconfig name="instance_id" configuration="instance_id" /> <label> <attr-connect name="label" variable="instance_id" /> </label> <label dir-for="label" path="/message" /> <label dir-for="description" path="/message" /> <hbox> <template path="/actions/*"> <button label="ok" dir-for="event,label"> <attr-connect name="label" function="last-dir" /> </button> </template> </hbox> </root>
Wykorzystujemy tutaj element fromconfig. Nazwa(name) oznacza nazwa zmiennej, która zostanie utworzona/nadpisana. Konfiguracja(configuration) to nazwa opcji konfiguracji klas okien. Później przypisujemy opcję konfiguracji do tekstu(label).
Czemu fromconfig?
Zarówno w shell-u, jak i klasach okien wykorzystałem konieczność odczytania konfiguracji. Czemu nie zrobiłem, jak kiedyś było w PHP, czyli automatyczne tworzenie zmiennych na podstawie argumentów przekazanych skryptowi, podczas uruchamiania? Bo to jest niebezpieczne, a poza tym może zepsuć skrypt/klasę okien. Po prostu konieczność odczytania konfiguracji powoduje, że w przypadku danej opcji konfiguracji powłoka zostanie wyświetlony odpowiedni komunikat. Poza tym, to przekazywane konfiguracji mogłoby spowodować, że skrypt/klasa okien nie będzie przygotowana na brak odpowiedniej opcji konfiguracji i robić błędne rzeczy w przypadku ich braku.