Mechanizmus pro automatické doplňování řetězců stojí na datové struktuře GCompletion
(viz níže), na kterou se volají všechny obslužné rutiny. K tomu, abyste mohli mechanizmus automatického doplňování řetězců používat, je nutné, abyste byli obeznámeni alespoň se základy práce s obousměrnými seznamy, o kterých se na Rootu psalo v minulých dvou dílech.
Doplňování řetězců funguje následovně. Speciálními funkcemi v GCompletion
u nejprve „zaregistrujete“ všechny možné (a smysluplné) řetězce. Voláním funkce g_completion_complete()
si pak vyžádáte uhodnutí konce řetězce k zadanému „začátku“ (prefixu – řetězci, který je zadán jako argument). Funkce prohledá seznam všech řetězců, vyhodnotí, jak by asi měl pokračovat a vrátí seznam všech vhodných adeptů na doplnění a popřípadě i nový prefix.
Podívejme se nyní trochu blíže na onu strukturu GCompletion
:
struct GCompletion { GList* items; GCompletionFunc func; gchar* prefix; GList* cache; };
items
prefix
Vytvoření nové struktury GCompletion
Abychom mechanizmus automatického doplňování řetězců mohli používat, je nutné nejprve vytvořit v paměti samotnou strukturu GCompletion
. Provede se to příkazem
GCompletion* g_completion_new(GCompletionFunc func);
Funkce požaduje jako argument konverzní funkci func
a vrátí pointer na nový GCompletion
.
Funkce func
je dána předpisem
gchar* (*GCompletionFunc)(gpointer);
Očekává se od ní, že jako jediný argument převezme gpointer
na data uložená v obousměrném seznamu a najde v nich řetězec, který předá jako svou návratovou hodnotu. Při takové práci musíte dbát na to, aby všechny řetězce, které funkce func
zprostředkovává, byly neustále v paměti. Mechanizmus ve funkci g_completion_complete()
to totiž pro správnou činnost vyžaduje. Nejsnáze se toho dosáhne tak, že v datech uložených v seznamu jsou přímo ony řetězce obsaženy a funkce func
pak pouze k těmto datům přistupuje (viz příklad na konci kapitoly). To, že by se řetězce při každém volání funkce func
teprve dynamicky tvořily a následně dealokovaly, je nepřípustné.
Budou-li v seznamu uloženy přímo řetězce, může být argument func
funkce g_completion_new()
NULL
ový.
Práce se seznamem „registrovaných“ položek
Se seznamem registrovaných položek (tedy s položkou items
struktury GCompletion
) se pracuje pomocí trojice následujících funkcí. Všechny tři požadují jako svůj první parametr cmp
pointer na strukturu GCompletion
.
void g_completion_add_items(GCompletion *cmp, GList *items);
…přidá mezi registrované prvky cmp->items
GCompletion
u cmp
prvky seznamu items
. Předchozí výsledky doplňování řetězce se zruší – tedy dealokují a na NULL
se nastaví cmp->prefix
i cmp->cache
.
Přidávání prvků proběhne tak, že se vytvoří kopie uzlů GList
seznamu do kterých se zkopírují pointery na jejich data
. Vyplývá z toho tedy to, že seznam items
musíte po použití sami dealokovat. Pozor však na data
, které budou mezi oběma seznamy sdílené. Musíte zajistit, aby po celou dobu, kdy je mechanizmus doplňování řetězců může potřebovat, existovaly.
Potřebujete-li ze seznamu registrovaných prvků cmp->items
naopak položky odstraňovat, použijte funkci
void g_completion_remove_items(GCompletion *cmp, GList
*items);
Tato funkce prohledá seznam registrovaných prvků ( cmp->items
) i seznam adeptů na doplnění řetězce ( cmp->cache
) a odstraní z nich všechny prvky, jejichž data
jsou v seznamu předaném jako parametr items
. Odstraněním prvků se rozumí dealokace uzlů GList
– s uloženými daty se nebude dít nic.
A konečně poslední funkce, která je velmi jednoduchá:
void g_completion_clear_items(GCompletion *cmp);
…jejím úkolem je odstranit všechny registrované položky a inicializovat GCompletion
cmp
, aby byl takový, jaký je po svém vytvoření – dealokují se celé seznamy cmp->items
i cmp->cache
stejně tak jako prefix cmp->prefix
. (Opět, aby bylo jasno: dealokací seznamu se rozumí vyprázdnění seznamu, tedy volání g_list_free()
– samotná data zůstanou v paměti nedotčena!)
Doplnění řetězce
Funkce
GList* g_completion_complete(GCompletion *cmp, gchar *prefix, gchar **new_prefix);
je klíčovou funkcí celého mechanizmu. Je to ona, kdo „odedře“ celou práci.
Jako první argument jí předejte pointer na GCompletion
( cmp
) a jako druhý argument „začátek“ řetězce neboli prefix
, ke kterému se bude provádět doplňování.
Funkce g_completion_complete()
vrátí seznam všech prvků, jejichž řetězce začínají zadaným prefix
em a do třetího argumentu ( new_prefix
) uloží nejdelší možný „začátek“ řetězce, který je společný pro všechny prvky shodující se se zadaným prefix
em nebo NULL
, jestliže nenalezne jedinou položku, která by začínala prefix
em.
Vrácený seznam je určen jen ke čtení a řetězec new_prefix
by měl být po použití dealokován.
Dealokace GCompletion
void g_completion_free(GCompletion *cmp);
…uvolní veškerou paměť použitou GCompletion
em cmp
včetně jeho seznamů. Datové položky seznamů však musíte dealokovat sami.
Příklad:
/* Priklad pouziti automatickeho doplnovani retezcu */ #include <glib.h> /* Datova struktura (abychom to nemeli tak jednoduche) */ typedef struct { gchar *s; gint value; /* Nejake dalsi polozky... */ } MyData; /* Konverzni funkce */ gchar* konverze(gpointer data) { return ((MyData *) data)->s; } /* Funkce pro vytvoreni dynamickych dat */ gpointer data_new(gchar* s, gint value) { MyData* data; data = g_new(MyData, 1); data->s = s; data->value = value; return (gpointer) data; } /* Tisk */ void data_print(gpointer data, gpointer user_data) { printf("%s - %d\n", ((MyData *) data)->s, ((MyData *) data)->value); } /* dealokace dat */ void data_dealloc(gpointer data, gpointer user_data) { g_free(data); } /* Hlavni program */ gint main(void) { GCompletion* cmp; GList* list = NULL; GList* completed; gchar* new_prefix; /* Vytvoreni noveho GCompletion */ cmp = g_completion_new(konverze); /* Registrace nejakych polozek */ list = g_list_append(list, data_new("dopis.txt", 2)); list = g_list_append(list, data_new("dluznici.txt", 8)); list = g_list_append(list, data_new("smlouva.txt", 13)); list = g_list_append(list, data_new("doporuceni.txt", 1)); list = g_list_append(list, data_new("dopis.gif", 6)); g_completion_add_items(cmp, list); /* Doplneni retezce */ completed = g_completion_complete(cmp, "k", &new_prefix); printf("Maximalni prefix: %s\n", new_prefix); g_list_foreach(completed, data_print, NULL); g_free(new_prefix); /* Doplneni retezce (podruhe) */ completed = g_completion_complete(cmp, "do", &new_prefix); printf("\nMaximalni prefix: %s\n", new_prefix); g_list_foreach(completed, data_print, NULL); g_free(new_prefix); /* Doplneni retezce (potreti) */ completed = g_completion_complete(cmp, "dopo", &new_prefix); printf("\nMaximalni prefix: %s\n", new_prefix); g_list_foreach(completed, data_print, NULL); g_free(new_prefix); /* Uvolneni pameti */ g_completion_free(cmp); g_list_foreach(list, data_dealloc, NULL); g_list_free(list); return 0; }
Spustíte-li si tuto ukázku, mělo by se vám vypsat:
Maximalni prefix: (null) Maximalni prefix: dop dopis.txt - 2 doporuceni.txt - 1 dopis.gif - 6 Maximalni prefix: doporuceni.txt doporuceni.txt - 1
Co to znamená, si jistě pilný čtenář prostuduje sám.
Jakékoliv dotazy a připomínky prosím směřujte na mou adresu.