QDBMにはC++言語用のAPIがある。QDBMの基本APIと拡張APIと上級APIの関数群をC++のクラス機構を用いてカプセル化し、かつスレッドセーフにしたものである。
基本APIはファイルを用いてハッシュデータベースを実現する。クラス `Depot' のコンストラクタによってデータベースファイルが開かれる。データベースを閉じるにはメンバ関数 `close' を呼ぶ。明示的にデータベースを閉じないでインスタンスが破棄される場合は、デストラクタによってデータベースが閉じられる。メンバ関数 `put' はレコードを追加するために用いる。メンバ関数 `out' はレコードを削除するために用いる。メンバ関数 `get' はレコードを検索するために用いる。その他にも、C言語の基本APIとほぼ同じ操作を利用することができる。各メンバ関数はエラー時にクラス `Depot_error' のインスタンスを投げる。
拡張APIはディレクトリと複数のファイルを用いてハッシュデータベースを実現する。クラス `Curia' のコンストラクタによってデータベースディレクトリが開かれる。データベースを閉じるにはメンバ関数 `close' を呼ぶ。明示的にデータベースを閉じないでインスタンスが破棄される場合は、デストラクタによってデータベースが閉じられる。メンバ関数 `put' はレコードを追加するために用いる。メンバ関数 `out' はレコードを削除するために用いる。メンバ関数 `get' はレコードを検索するために用いる。ラージオブジェクトを扱うこともできる。その他にも、C言語の拡張APIとほぼ同じ操作を利用することができる。各メンバ関数はエラー時にクラス `Curia_error' のインスタンスを投げる。
上級APIはファイルを用いてB+木データベースを実現する。クラス `Villa' のコンストラクタによってデータベースファイルが開かれる。データベースを閉じるにはメンバ関数 `close' を呼ぶ。明示的にデータベースを閉じないでインスタンスが破棄される場合は、デストラクタによってデータベースが閉じられる。メンバ関数 `put' はレコードを追加するために用いる。メンバ関数 `out' はレコードを削除するために用いる。メンバ関数 `get' はレコードを検索するために用いる。その他にも、C言語の上級APIとほぼ同じ操作を利用することができる。各メンバ関数はエラー時にクラス `Villa_error' のインスタンスを投げる。
`Depot' と `Curia' と `Villa' はクラス `ADBM' の派生クラスである。このクラスはUNIX標準のDBMと同様の機能を持つデータベースマネージャを抽象化したインタフェースであり、純粋仮想関数の宣言のみを行う。各メンバ関数はエラー時にクラス `DBM_error' のインスタンスを投げる。このフレームワークでは、レコードのキーや値はクラス `Datum' として表現される。`Datum' のインスタンスはデータ領域のポインタとそのサイズを持つ。四つのAPIから適切なものを選択する際には、実行効率を重視するなら `Depot' を、スケーラビリティを重視するなら `Curia' を、順序に基づく参照が必要なら `Villa' を、エレガンスと保守性を重視するなら `ADBM' を選ぶべきであろう。データベースファイルは各APIの間で互換性がない。
アプリケーションのソースファイルにおいて、`Depot' を用いるには `xdepot.h' を、`Curia' を用いるには `xcuria.h' を、`Villa' を用いるには `xvilla.h' を、`ADBM' を用いるには `xadbm.h' をインクルードする必要がある。各クラスは名前空間 `qdbm' にパッケージされている。
C言語のAPIは、スレッド間でデータベースハンドルを共有しない限りはスレッドセーフである。C++のAPIでは、複数のスレッドが同じハンドルにアクセスしてもスレッドセーフである。このAPIはPOSIXスレッドパッケージを前提として実装されている。別のスレッドパッケージを用いる場合は、独自のC++用ラッパを書くべきである。
`put' で既存のレコードの上書きがキャンセルされた際や `get' で存在しないレコードが検索された際には例外によって操作の失敗が通知されるが、それが鬱陶しい場合は `silent' フラグを真にするとよい。その場合は失敗が戻り値によって通知される。
APIの詳細に関しては、サブディレクトリ `xapidoc' の文書と各ヘッダファイルを参照すること。
GCCの3.3以降のバージョンがインストールされ、QDBMが `/usr/local' 以下にインストールされていることが必要である。
インストール作業は、サブディレクトリ `plus' をカレントディレクトリにして行う。
cd plus
ビルド環境を設定する。
./configure
プログラムをビルドする。
make
プログラムの自己診断テストを行う。
make check
プログラムをインストールする。作業は `root' ユーザで行う。
make install
一連の作業が終ると、ヘッダファイル `xdepot.h' と `xcuria.h' と `xvilla.h' と `xadbm.h' が `/usr/local/include' に、ライブラリ `libxqdbm.so' 等が `/usr/local/lib' に、コマンド `xdptest' と `xcrtest' と `xvltest' が `/usr/local/bin' にインストールされる。
アンインストールするには、`./configure' をした後の状態で以下のコマンドを実行する。作業は `root' ユーザで行う。
make uninstall
Windows(Cygwin)にインストールする場合、以下の手順に従う。
ビルド環境を設定する。
./configure
プログラムをビルドする。
make win
プログラムの自己診断テストを行う。
make check-win
プログラムをインストールする。なお、アンインストールする場合は `make uninstall-win' とする。
make install-win
Windowsでは、静的ライブラリ `libxqdbm.a' の代わりにインポートライブラリ `libxqdbm.dll.a' が生成され、さらにダイナミックリンクライブラリ `xqdbm.dll' が生成される。`xqdbm.dll' は `/usr/local/bin' にインストールされる。
C++用のAPIではMinGWを用いたビルドはサポートされない。
Mac OS X(Darwin)にインストールする場合、以下の手順に従う。
ビルド環境を設定する。
./configure
プログラムをビルドする。
make mac
プログラムの自己診断テストを行う。
make check-mac
プログラムをインストールする。なお、アンインストールする場合は `make uninstall-mac' とする。
make install-mac
Mac OS Xでは、`libxqdbm.so' 等の代わりに `libxqdbm.dylib' 等が生成される。
HP-UXにインストールする場合、以下の手順に従う。
ビルド環境を設定する。
./configure
プログラムをビルドする。
make hpux
プログラムの自己診断テストを行う。
make check-hpux
プログラムをインストールする。なお、アンインストールする場合は `make uninstall-hpux' とする。
make install-hpux
HP-UXでは、`libxqdbm.so' 等の代わりに `libxqdbm.sl' が生成される。
名前と対応させて電話番号を格納し、それを検索するアプリケーションのサンプルコードを以下に示す。
#include <xdepot.h>
#include <cstdlib>
#include <iostream>
using namespace std;
using namespace qdbm;
const char* NAME = "mikio";
const char* NUMBER = "000-1234-5678";
const char* DBNAME = "book";
int main(int argc, char** argv){
  try {
    // データベースを開く
    Depot depot(DBNAME, Depot::OWRITER | Depot::OCREAT);
    // レコードを格納する
    depot.put(NAME, -1, NUMBER, -1);
    // レコードを取得する
    char* val = depot.get(NAME, -1);
    cout << "Name: " << NAME << endl;
    cout << "Number: " << val << endl;
    free(val);
    // データベースを閉じる
    depot.close();
  } catch(Depot_error& e){
    cerr << e << endl;
    return 1;
  }
  return 0;
}
上記の例を `ADBM' クラスを用いて書き直した例を以下に示す。
#include <xadbm.h>
#include <xdepot.h>
#include <iostream>
using namespace std;
using namespace qdbm;
const char* NAME = "mikio";
const char* NUMBER = "000-1234-5678";
const char* DBNAME = "book";
int main(int argc, char** argv){
  try {
    // データベースを開く
    Depot depot(DBNAME, Depot::OWRITER | Depot::OCREAT);
    ADBM& dbm = depot;
    // レコードを準備する
    Datum key(NAME);
    Datum val(NUMBER);
    // レコードを格納する
    dbm.storerec(key, val);
    // レコードを取得する
    const Datum& res = dbm.fetchrec(key);
    cout << "Name: " << NAME << endl;
    cout << "Number: " << res << endl;
    // データベースを閉じる
    dbm.close();
  } catch(DBM_error& e){
    cerr << e << endl;
    return 1;
  }
  return 0;
}
`Villa' クラスを用いて文字列の前方一致検索を行う例を以下に示す。
#include <xvilla.h>
#include <cstdlib>
#include <cstring>
#include <iostream>
using namespace std;
using namespace qdbm;
const char* DBNAME = "words";
const char* PREFIX = "apple";
int main(int argc, char** argv){
  try {
    // データベースを開く
    Villa villa(DBNAME, Villa::OWRITER | Villa::OCREAT);
    // レコードを格納する
    villa.put("applet", -1, "little application", -1, Villa::DDUP);
    villa.put("aurora", -1, "polar wonderwork", -1, Villa::DDUP);
    villa.put("apple", -1, "delicious fruit", -1, Villa::DDUP);
    villa.put("amigo", -1, "good friend", -1, Villa::DDUP);
    villa.put("apple", -1, "big city", -1, Villa::DDUP);
    try {
      // カーソルを候補の先頭に置く
      villa.curjump(PREFIX, -1);
      // カーソルを走査する
      for(;;){
        char* key = villa.curkey();
        if(strstr(key, PREFIX) != key){
          free(key);
          break;
        }
        char* val = villa.curval();
        cout << key << ": " << val << endl;
        free(key);
        free(val);
        villa.curnext();
      }
    } catch(Villa_error& e){
      if(e.code() != Villa::ENOITEM) throw e;
    }
    // データベースを閉じる
    villa.close();
  } catch(Villa_error& e){
    cerr << e << endl;
    return 1;
  }
  return 0;
}
C++用APIを利用したプログラムをビルドするには、ライブラリ `xqdbm' と `qdbm' をリンク対象に加える必要がある。例えば、`sample.cc' から `sample' を作るには、以下のようにビルドを行う。
g++ -I/usr/local/include -o sample sample.cc -L/usr/local/lib -lxqdbm -lqdbm
ひとつのプロセスで複数のデータベースファイルを同時に利用することは可能であるが、同じデータベースファイルの複数のハンドルを利用してはならない。ひとつのデータベースを複数のスレッドで利用する場合には、メインスレッドで生成したハンドルを他のスレッドに渡せばよい。
一般的なオブジェクトを直列化してデータベースに格納する仕組みはないので、必要ならば、`Datum' クラスを拡張してその仕組みを作ってほしい。また、`Villa' の比較関数で、直列化したオブジェクトを復元した上で比較するようにすれば、よりオブジェクト指向データベースらしくなるだろう。