概要
ここではglibの使い方を例と共に紹介します。
ハッシュ
glibにはgenericなhash操作のための関数群があります。内容的にはこれと言った特徴はなさそうですが、ちょこっとhashが欲しい時に一々コーディングする必要がなくて楽ちんです。
hashの生成
hashを生成するには、まず
- hash関数
- 比較関数
を用意します。
hash関数
hash関数は、キーから整数への写像を作るものです。hash関数は以下の ような形式になっています。
typedef guint (*GHashFunc)(gconstpointer key);
ですから、実際の関数の定義は以下の例のようになります。
static guint
Hash(
gconstpointer key)
{
char *p;
guint ret;
ret = 0;
if ( key != NULL ) {
for ( p = (char *)key ; *p != 0 ; p ++ ) {
ret = ( ret << 4 ) | *p;
}
}
return (ret);
}
この例では、単にシフトしながら文字を加算するだけですが、必要ならより colisionの発生の少ない関数を考えます。
比較関数
比較関数とは、keyが一致したことを判定するための関数です。つまり、hashテーブル上のkeyと、検索時に指定したキーが一致するということを判定するために使われます。
理想的なhash関数なら、キー値が異なればhash値も異なるということに なり、一々比較をするまでもないはずですが、そのような写像を作るのは困難であるため、
とりあえず適当でいーじゃん
ということでhash関数を作り、キー値が異なっても同じhash関数を返してしまうということは、「あること」として扱うようにします。具体的にはhash値でバケツのようなものを作っておき、同じhash値のものは同じバケツの中に放り込んでおくようにするわけです。
このバケツの中を探すために、実際のキー値の比較を行います。このための関数が比較関数になります。
現在のglibでは、バケツの中を探すためには、あまり複雑なやり方をしていないため(単純な馬鹿サーチをしています)、単にキー値が等しいかどうかの判定だけを行えば良いようです。比較関数は以下のような形式です。
typedef gint (*GCompareFunc)(gconstpointer a,
gconstpointer b);
ですから、実際の関数の定義は以下の例のようになります。
static gint
Compare(
gconstpointer s1,
gconstpointer s2)
{
return (!strcmp((char *)s1,(char *)s2));
}
ここで注意しなくてはならないことは、この関数は一致していたら、真を返さないといけないということです。普通にstrcmpすると、「一致したら偽」になってしまいますから、注意しましょう。この比較関数の形式は他の表管理での比較にも使われますので、それらに流用することを考えれば、単に等しいかどうかだけではなくそうかと思ってしまうわけですが、他の表管理では< = >の意味として、負 0 正を使うようになっているものが多いですから、流用は出来ません。
hashの生成
関数の用意が出来たら、hashを生成します。hashの生成するための関数はg_hash_table_newで、以下のような定義です。
GHashTable* g_hash_table_new(GHashFunc hash_func,
GCompareFunc key_compare_func);
ですから、実際のhashの生成は以下の例のようになります。
static GHashTable *HashTable = NULL;
.....
if ( HashTable != NULL )
g_hash_table_destroy(HashTable);
HashTable = NULL;
}
HashTable = g_hash_table_new((GHashFunc)Hash,
(GCompareFunc)Compare); /* ※ */
この例で最初のif文は、hashを何度も作ることになった時のための処理ですから、不要なこともあると思います。実際にhashを生成しているのは※の部分です。
これでhashの生成が出来ました。
hashの破壊
hashの生成の例で破壊の話も出て来たので、ここで説明し ておきます。
hashを破壊するための関数はg_hash_destroyで、以下のような定義です。
void g_hash_table_destroy(GHashTable *hash_table);
この関数は、hashの破壊のみを行い、格納されたデータには影響を与えません。また、後で述べるデータの格納の時には、単にhashのデータ構造には、keyや実体へのポインタのみを格納しています。つまり、hashのデータ構造は、格納したデータの「糊」としてのみ働いています。そして、この関数ではその「糊」の部分だけを破壊します。このことに留意して下さい。
具体的な使用例については、hashの生成の例を見て下さい。
hashの検索
hashを検索するための関数は、g_hash_table_lookupです。
gpointer g_hash_table_lookup(GHashTable *hash_table,
gconstpointer key);
hash_tableは生成しデータを格納したhash、keyは検索のためのキーです。返り値が検索結果へのポインタです。この辺のことは、次の hashへの格納も一緒に見て下さい。
実際のhashの検索は以下の例のようになります。
if ( ( body = g_hash_table_lookup(HashTable,name) )
== NULL ) {
この例でわかるように、発見出来なかった場合はNULLが返ります。
hashへの格納
hashの格納のための関数は、g_hash_table_insertです。
void g_hash_table_insert(GHashTable *hash_table,
gpointer key,
gpointer value);
hash_tableは生成したhash、keyは検索のためのキー、valueが実際に格納される実態です。なお、gpointerはvoid *と同じ意味なので、keyもvalueも好きなものが入れられます。逆に言えば、glib側では何の処理もしないで、単なるポインタとしてのみ操作するということでもあります。
破壊のところでも述べたように、格納では一切のデータの複製を行っていません。特に注意しなくてはならないのは、keyです。hashにはキーのポインタのみが格納されますから、
同じデータ領域使い回すことは出来ません。
注意しましょう。
実際のhashへの格納は以下の例のようになります。
if ( g_hash_table_lookup(HashTable,name) == NULL ) {
g_hash_table_insert(HashTable,name,entry); /* ※ */
}
この例では重複登録を避けるために、一度検索して存在しないことを確認してから格納を行っています。
hash全データへの処理
glibではhashの全データに対する処理の方法が提供されています。このための関数が、g_hash_table_foreachです。
guint g_hash_table_foreach_remove(
GHashTable *hash_table,
GHRFunc func,
gpointer user_data);
ここで、実際に処理を行う手続きは、funcで与えられます。funcの形式は、
typedef gboolean (*GHRFunc)(gpointer key,
gpointer value,
gpointer user_data);
となります。この手続きの引数のkeyは処理中のデータのキー値、valueには処理 するべきデータ(へのポインタ)、user_dataはg_hash_table_foreach_removeを呼び出す時のuser_dateです。この関数はgbooleanの返り値があることになっていますが、実際に使用されていません。以下が処理関数の例です。
static gboolean
PrintEntry(
gpointer key,
gpointer value,
gpointer user_data)
{
GtkWidget *widget;
gchar *entry_text;
widget = glade_xml_get_widget(CurrentXML,key);
entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
printf("Entry contents: %s = [%s]n",
(char *)key,entry_text);
return(0);
}
実際に処理をする時には、
g_hash_table_foreach(HashTable,(GHFunc)PrintEntry,NULL);
のように呼び出します。
hashからの削除
hashからエントリを削除するための関数は、g_hash_table_removeです。hashは要素の削除は結構面倒だったりするので、この関数はかなり便利です。
void g_hash_table_remove(GHashTable *hash_table,
gconstpointer key);
hash_tableは生成したhash、keyは削除する要素のキーです。
破壊のところでも述べたように、格納では一切のデータの複製を行っていませんので、削除処理で消えるのは、hashのデータ構造のみです。
実際のhashからの削除は以下の例のようになります。
g_hash_table_remove(HashTable, key);
なお、g_hash_table_removeは一回の呼び出しで削除される要素は1つだけです。キーを重複して登録してしまった場合は、複数回呼び出す必要があります。また、返り値は持ちません。キーに該当するエントリがない時も、特に通知はありません。