Hlavní navigace

GLib: Klíčované seznamy dat

27. 10. 2000
Doba čtení: 4 minuty

Sdílet

Pokud seriál článků o knihovně GLib sledujete pravidelně, setkali jste se už s "nafukovacími" řetězci a poli automaticky měnícími svou velikost podle potřeby a podobně. Dnes se vám pokusím představit něco jako "nafukovací" strukturu (typ podobný záznamům struct). Její prvky mohou ale měnit svůj typ, velikost i počet.

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 e­xistuje 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()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

CS24 tip temata

#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.

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.