Hlavní navigace

GLib: Klíčované seznamy dat (2)

Michal Burda

Dokončení výkladu o klíčovaných seznamech dat.

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 GSListseznam 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_per­sonal(), 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_per­sonal() 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.

Našli jste v článku chybu?
Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

Vitalia.cz: Vady očí, o kterých nevíme, jsou nejhorší

Vady očí, o kterých nevíme, jsou nejhorší

Podnikatel.cz: 3, 2, 1..EET startuje. Na co nezapomenout?

3, 2, 1..EET startuje. Na co nezapomenout?

Lupa.cz: Proč Stream netočí jen Kancelář Blaník?

Proč Stream netočí jen Kancelář Blaník?

DigiZone.cz: Co chtějí operátoři při přechodu na DVB-T2?

Co chtějí operátoři při přechodu na DVB-T2?

Podnikatel.cz: Chaos u EET pokračuje. Jsou tu další návrhy

Chaos u EET pokračuje. Jsou tu další návrhy

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

Lupa.cz: Kdo pochopí vtip, může jít do ČT vyvíjet weby

Kdo pochopí vtip, může jít do ČT vyvíjet weby

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

Měšec.cz: mBank cenzuruje, zrušila mFórum

mBank cenzuruje, zrušila mFórum

Vitalia.cz: Mondelez stahuje rizikovou čokoládu Milka

Mondelez stahuje rizikovou čokoládu Milka

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

Vitalia.cz: Proč vás každý zubař posílá na dentální hygienu

Proč vás každý zubař posílá na dentální hygienu

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Lupa.cz: Teletext je „internetem hipsterů“

Teletext je „internetem hipsterů“

Měšec.cz: Air Bank zruší TOP3 garanci a zdražuje kurzy

Air Bank zruší TOP3 garanci a zdražuje kurzy

120na80.cz: Co všechno ovlivňuje ženskou plodnost?

Co všechno ovlivňuje ženskou plodnost?

Měšec.cz: Kdy vám stát dá na stěhování 50 000 Kč?

Kdy vám stát dá na stěhování 50 000 Kč?