Klíčovaný seznam dat (Keyed Data List) je vlastně seznam uchovávající různé datové prvky, k nimž je přístup buď pomocí řetězce nebo zadáním jeho GQuark
u (viz minulý díl článku o knihovně GLib).
Na klíčované seznamy dat se můžete dívat jako na jakési záznamy (céčkařské struktury struct
) se schopností dynamicky měnit počet a typ svých prvků.
Nástroj má v sobě navíc vestavěn systém automatického dealokování dat. Když je uživatel chce odstranit nebo změnit, staré údaje se samočinně uvolní z paměti.
Každá položka klíčovaného seznamu si vnitřně uchovává identifikační číslo klíče ( GQuark
), data a pointer na funkci, kterou se mají data dealokovat. Podle potřeby se touto funkcí uchovávaná data automaticky dealokují. Je-li pointer na dealokovací funkci nastaven na NULL
, uvolňování z paměti se přeskakuje.
Klíčované seznamy dat reprezentuje speciální typ:
struct GData;
Všechny prvky struktury GData
jsou neveřejné a práce s typem by měla probíhat výhradně níže popsanými funkcemi.
Vytvoření nového klíčovaného seznamu dat
Pointer uchovávající klíčovaný seznam dat se inicializuje funkcí
void g_datalist_init(GData **datalist);
Argument datalist
je pointer na pointer na GData
(reference na pointer klíčovaného seznamu – &
). Funkce g_datalist_init()
jednoduše předaný pointer nastaví na NULL
. Žádné uvolňování paměti vyhrazené pro předchozí data se nekoná, takže touto funkcí proměnnou inicializujte pouze na začátku před prvním použitím.
Příklad:
... /* deklarace */ GData *my_datalist; /* inicializace */ g_datalist_init(&my_datalist); /* nyni plati: (my_datalist == NULL) */ ...
Ačkoliv byste stejného efektu dosáhli prostým zapsáním
my_datalist = NULL;
…raději přesto používejte g_datalist_init()
. Vaše programy tak budou určitě přehlednější a navíc se stáváte imunními vůči možným budoucím změnám v kódu knihovny GLib ze strany jejich autorů (kdo ví, co s tím dále zamýšlejí! :-).
Přidávání prvků do klíčovaného seznamu dat
Jak již bylo dříve naznačeno, k prvkům klíčovaného seznamu lze přistupovat pomocí identifikačního řetězce nebo jeho GQuark
u. Funkce pro přidávání prvků se proto dělí do dvou kategorií: jedny pracují s GQuark
y a druhé s řetězci.
„Kvarkovou“ verzí je třeba funkce:
void g_datalist_id_set_data_full(GData **datalist, GQuark key_id, gpointer data, GDestroyNotify destroy_func);
…která do klíčovaného seznamu datalist
pod klíč daný GQuark
em key_id
uloží data data
a pro jejich případné pozdější dealokování si zaregistruje funkci destroy_func
. Jestliže prvek s klíčem key_id
v seznamu již existuje, uvolní ho nejprve z paměti voláním jeho vlastní dealokovací funkce.
Má-li prvek jako destroy_func
zaregistrovanou hodnotu NULL
, znamená to pro knihovnu GLib, že příslušná data jsou statická a dealokování se přeskočí.
Jinými slovy, funkce g_datalist_id_set_data_full()
se nejprve „podívá“, jestli v datalist
u existuje záznam s klíčem key_id
. Pokud ano a jeho destroy_func
není NULL
, stará data jí dealokuje. Následuje uložení nových dat ( data
) a zaregistrování nové dealokovací funkce destroy_func
.
Zadáváte-li jako destroy_func
neustále NULL
, můžete pro úsporu psaní použít makro:
#define g_datalist_id_set_data(dl, q, d)
dl
Chcete-li k identifikaci položek použít přímo řetězce (což je mimochodem sice komfortnější, ale pomalejší způsob), volejte místo předchozích dvou rutin následující makra:
#define g_datalist_set_data(dl, k, d)
V obou případech dl
znamená pointer na pointer na klíčovaný seznam, d
jsou ukládaná data a f
u druhého makra pointer na dealokovací funkci. Místo GQuark
u se však zadává jako parametr k
přímo řetězcový klíč.
Makra se rozvinou do volání funkce g_quark_from_string()
(viz. předcházející díl článku pojednávající o kvarcích), která řetězec k
přeloží na číselnou hodnotu ( GQuark
) a ta se předá spolu s dalšími parametry funkcím g_datalist_id_set_data()
a g_datalist_id_set_data_full()
.
Ještě zmíním něco o dealokovacích funkcích. Jejich funkční prototyp musí odpovídat typu GDestroyNotify
:
void (*GDestroyNotify) (gpointer data);
Musí to být funkce požadující jediný parametr – dealokovaná data. Ve většině případů budete asi používat přímo funkci g_free()
.
Příklad:
#include <glib.h>
gint main(void) { GData *zbozi; GQuark qnazev; qnazev = g_quark_from_static_string("Nazev"); g_datalist_init(&zbozi); /* nasledujici volani jsou ekvivalentni */ g_datalist_id_set_data_full(&zbozi, qnazev, "Red Hat Linux", NULL); g_datalist_set_data(&zbozi, "Nazev", "Debian Linux"); /* ... */ }
V uvedeném příkladu jsou si poslední dva volání funkcí g_datalist_id_set_data_full()
a g_datalist_set_data()
svým účinkem rovny (druhá varianta je jen o něco pomalejší). Do položky "Nazev"
datalistu zbozi
se uloží nejprve řetězec "Red Hat Linux"
, který se vzápětí nahradí řetězcem "Debian Linux"
. Dealokování uložených řetězců nehrozí, protože obě varianty nastavují jako „dealokovací“ funkci NULL
.
Všimněte si prosím také konvence, která je zachována u všech funkcí pracující s klíčovanými seznamy dat. Do parametru funkcí představující zpracovávaný klíčovaný seznam se ukládá pointer na pointer (adresa pointeru). Referenční operátor &
proto využijete opravdu hojně.
Získávání údajů z klíčovaného seznamu dat
gpointer g_datalist_id_get_data(GData **datalist, GQuark key_id);
…vrátí data uložená v klíčovaném seznamu datalist
(pozor na to, že jako parametr se opět očekává pointer na pointer na GData
) pod klíčem key_id
.
Očekávatelnou analogií funkce g_datalist_id_get_data()
je makro
#define g_datalist_get_data(dl, k)
…,které se používá pro adresaci dat klíčovaného seznamu dl
(pointer na pointer, jak jinak) řetězcem k
. Použití tohoto makra je pomalejší, protože řetězec k
se musí navíc „přeložit“ na GQuark
.
A to je pro dnešek vše. Příště si ukážeme nějaké další funkce a trochu delší příklad, na kterém si všechno procvičíme.