僕の主戦場はもっぱらWindowsですけど、Linux由来のコードやライブラリのお世話になることも少なくありません。先日もファイルに保存可能な辞書(key-value store)を探してて、GDBM:GNU Database ManagerのWindows版を見つけました。マジなDatabase使うほど大層なものではなく、キーと値の組をファイルに保存し、キーから値が検索できれば十分というシチュエーションだったので、このWindows-port版GDBM、ありがたく使わせていただきました。
GDBMをハダカで使う
GDBMのトリセツはman pageを一読していただくことにして、まずはそのまま、C-interfaceでお試しコードを書いてみましょう。
#include <iostream> #include <cassert> #include <array> #include <cstring> // strlen #include <cstdlib> // free #define GDBM_STATIC #include <gdbm.h> using namespace std; void store() { GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_NEWDB, 0644, 0); assert( dbf != nullptr ); array<const char*,4> keys = { "apple", "orange", "grape", "apple" }; array<const char*,4> contents = { "まっく", "みかん", "ぶどう", "りんご" }; datum key; datum content; for ( size_t i = 0; i < keys.size(); ++i ) { // キーと値をdatumにセット key.dptr = const_cast<char*>(keys[i]); key.dsize = strlen(keys[i]) + 1; content.dptr = const_cast<char*>(contents[i]); content.dsize = strlen(contents[i]) + 1; // キーと値のペアを登録 // ※ GDBM_REPLACEにより、appleに紐づく値は // ("まっく"から"りんご"に)上書きされます int result = gdbm_store(dbf, key, content, GDBM_REPLACE); assert( result == 0 ); } gdbm_close(dbf); cout << "3 entries stored." << endl; } void fetch() { GDBM_FILE dbf = gdbm_open("trial.db", 512, GDBM_WRCREAT, 0644, 0); assert( dbf != nullptr ); array<const char*,4> keys = { "apple", "orange", "grape", "peach" }; datum key; datum content; for ( const char* target : keys ) { // キーをdatumにセットし、 key.dptr = const_cast<char*>(target); key.dsize = strlen(target) + 1; // キーに紐づいた値を検索 content = gdbm_fetch(dbf, key); if ( content.dptr == nullptr ) { cout << target << " not found..." << endl; } else { cout << target << " = " << content.dptr << endl; free(content.dptr); } } // Database内の全エントリを列挙する cout << "--- list all entries ---" << endl; key = gdbm_firstkey(dbf); while ( key.dptr != nullptr ) { char* tmp = key.dptr; content = gdbm_fetch(dbf, key); cout << key.dptr << " = " << content.dptr << endl; free(content.dptr); key = gdbm_nextkey(dbf, key); free(tmp); } } int main() { store(); fetch(); }
関数 store()ではキーと値のペアの登録、fetch()ではキーに紐づいた値の検索と、全エントリの列挙を行っています。GDBMが扱うデータはキー/値のいずれもデータの先頭アドレスとデータの長さ、すなわちバイト列を対象としていますから、(ここでは文字列をキー/値としていますけど)任意の型をキー/値として利用できます。気をつけなきゃならないのは、GDBMから帰ってきたdatum内のdptrが指すのはmalloc()で確保されたメモリ・ブロックであり、これをfree()するのは利用者の責任であることです。free()を忘れると少しずつメモリを食いつぶすことになります。