Než se pustíme do dalšího výkladu, připomenu ještě úmluvu, že všechny parametry uchovávající zpracovávaný klíčovaný seznam vyžadují pointer na pointer na daný seznam.
Odstraňování prvků z klíčovaného seznamu dat
#define g_datalist_id_remove_data(dl, q)
…odstraní (a podle potřeby i dealokuje) z klíčovaného seznamu dl
(pointer na pointer) prvek s klíčem odpovídajícím kvarku q
.
#define g_datalist_remove_data(dl, k)
…rovněž odstraňuje z datalistu dl
položku s daty, ale místo kvarku jako klíč požaduje řetězec (který se vnitřně na kvark převede).
Pokud položka s klíčem q
(popř. k
) v datalistu dl
neexistuje, nestane se nic.
Potřebujete-li z klíčovaného seznamu odstranit prvek tak, aby se jeho data nedealokovala (ačkoliv v normálním případě by se tak stalo), vyberte si k zavolání následující funkci nebo makro:
void g_datalist_id_remove_no_notify(GData **datalist, GQuark key_id); #define g_datalist_remove_no_notify(dl, k)
Funkce g_datalist_id_remove_no_notify()
odstraní (avšak nedealokuje) z klíčovaného seznamu datalist
prvek s klíčem odpovídajícím kvarku key_id
a makro g_datalist_remove_no_notify()
udělá totéž se seznamem dl
, ale prvek rozezná podle řetězcového klíče k
.
Chcete-li najednou zničit všechny položky klíčovaného seznamu, zavolejte na něj funkci
void g_datalist_clear(GData **datalist);
Tímto se z klíčovaného seznamu všechna data dealokují i odstraní a datalist
bude znovu čistý jako děťátko (a navíc nastaven na NULL
).
Konstrukce „foreach“
Chcete-li snad nějakou rutinu vykonat na všech prvcích klíčovaného seznamu, můžete tak učinit voláním funkce
void g_datalist_foreach(GData **datalist, GDataForeachFunc func, gpointer user_data); void (*GDataForeachFunc) (GQuark key_id, gpointer data, gpointer user_data);
Funkce g_datalist_foreach()
zavolá na každý prvek klíčovaného seznamu datalist
uživatelskou funkci func
, které předá kvark key_id
identifikující daný prvek, jeho data data
a další uživatelská data user_data
. Uživatelská funkce musí odpovídat prototypu GDataForeachFunc
.
Příklad:
K pochopení tohoto příkladu je nutné být obeznámen s jednosměrnými seznamy.
/* Jednoducha databaze telefonnich cisel */
#include <glib.h>
/* makro pro pridani retezcovych dat do GData */ #define my_set_string_data(dl, k, s) g_datalist_set_data_full((dl), (k), (gpointer) g_strdup(s), g_free); /* pridani zamestnance */ void my_append_work(GSList **seznam, gchar *jmeno, gchar *funkce, gchar *teldomu, gchar *telprace, gfloat plat) { GData *polozka; gfloat *pplat; g_datalist_init(&polozka); g_datalist_set_data(&polozka, "typ", GINT_TO_POINTER(1)); my_set_string_data(&polozka, "jmeno", jmeno); my_set_string_data(&polozka, "funkce", funkce); my_set_string_data(&polozka, "teldomu", teldomu); my_set_string_data(&polozka, "telprace", telprace); pplat = g_malloc(sizeof(gfloat)); *pplat = plat; g_datalist_set_data_full(&polozka, "plat", (gpointer) pplat, g_free); *seznam = g_slist_append(*seznam, (gpointer) polozka); } /* pridani znameho */ void my_append_personal(GSList **seznam, gchar *jmeno, gchar *adresa, gchar *telefon) { GData *polozka; g_datalist_init(&polozka); g_datalist_set_data(&polozka, "typ", GINT_TO_POINTER(2)); my_set_string_data(&polozka, "jmeno", jmeno); my_set_string_data(&polozka, "adresa", adresa); my_set_string_data(&polozka, "telefon", telefon); *seznam = g_slist_append(*seznam, (gpointer) polozka); } /* funkce pro tisk polozky seznamu */ void my_print_slist(gpointer data, gpointer user_data) { GData *polozka; gfloat *plat; polozka = (GData *) data; if (GPOINTER_TO_INT(g_datalist_get_data(&polozka, "typ")) == 1) { /* tiskne se pracovnik */ plat = g_datalist_get_data(&polozka, "plat"); printf("Pracovnik: %s, %s; tel: %s, domu: %s; plat: %0.2f Kc ", g_datalist_get_data(&polozka, "jmeno"), g_datalist_get_data(&polozka, "funkce"), g_datalist_get_data(&polozka, "telprace"), g_datalist_get_data(&polozka, "teldomu"), *plat); } else { /* tiskne se znamy */ printf("Znamy: %s; %s; tel: %s ", g_datalist_get_data(&polozka, "jmeno"), g_datalist_get_data(&polozka, "adresa"), g_datalist_get_data(&polozka, "telefon")); } } /* dealokace dat */ void my_free_item(gpointer data, gpointer user_data) { GData *polozka; polozka = (GData *) data; g_datalist_clear(&polozka); } /* hlavni telo programu */ gint main(void) { GSList *seznam = NULL; /* inicializace seznamu */ my_append_work(&seznam, "Josef Novak", "manazer", "02/123456", "11", 20000); my_append_personal(&seznam, "Karel Kvetinac", "Polni 32, Praha", "02/12126385"); my_append_personal(&seznam, "Petr Novy", "Akatova 1, Liberec", "026/55443"); my_append_personal(&seznam, "Pavel Stary", "Hlavni 13, Brno", "03/229933"); my_append_work(&seznam, "Tomas Lenochod", "delnik", "nema", "21", 8000); my_append_personal(&seznam, "Hana Fialova", "Vodni 13/a, Ostrava", "0609/113114"); /* ... */ printf("V databazi je celkem %d zaznamu: ", g_slist_length(seznam)); /* tisk seznamu */ g_slist_foreach(seznam, my_print_slist, NULL); /* dealokace */ g_slist_foreach(seznam, my_free_item, NULL); g_slist_free(seznam); }
Uvedený příklad implementuje jednoduchý telefonní seznam, který je tvořen jednosměrným seznamem GSList
. Do GSList
u seznam
jsou za sebou ukládány „nafukovací“ datové záznamy GData
.
Příklad je dlouhý, ale ne složitý. Na začátku main()
se seznam kontaktů inicializuje. Použil jsem pro tyto účely dvě speciální funkce my_append_work()
a my_append_personal()
, které vytvoří jeden záznam a ten uloží do seznam
u.
Pro tisk seznamu jsem použil konstrukce „foreach“ GSList
ů. Na každý prvek seznamu se zavolá funkce my_print_slist()
, která se postará o vytisknutí jednoho záznamu na obrazovku.
Nakonec se celý seznam dealokuje. Před uvolněním samotného GSList
u se ale musí dealokovat jeho data (záznamy GData
). Pro tyto účely jsem opět využil konstrukce „foreach“, která tentokrát volá funkci my_free_item()
a ta uvolní paměť zabíranou prvky jednoho záznamu.
Funkce my_append_work()
vytvoří z předaných parametrů záznam o osobě typu „pracovník“ a ten uloží do seznam
u. K vytvoření klíčovaného seznamu polozka
se využívá pomocné makro my_set_string_data()
. Jeho účelem je vytvořit kopii předaného řetězce a tu uložit do záznamu. Všimněte si, že jako „dealokovací“ funkce je použito g_free
.
Funkce my_append_personal()
slouží k vytvoření záznamu o „známé“ osobě (ne pracovník). Všimněte si, že jejich uchovávaná data jsou dramaticky různá od „pracovníků“.
Funkce pro tisk kontaktu, my_print_slist()
, nejprve podle prvku "typ"
záznamu polozka
určí, o jaký typ dat jde („pracovník“ nebo „známý“) a podle toho vytiskne příslušné informace na obrazovku.
Funkce my_free_item()
sloužící k dealokování dat seznamu jednoduše na každou GData
položku seznamu seznam
volá g_datalist_clean()
, která se postará o správnou dealokaci.
Uvedený příklad prosím chápejte pouze jako ilustraci použití klíčovaných seznamů a ne jako optimální řešení daného problému.