薄いwrapperでくるむ
"はじめの一歩"として、GDBMをうすーいクラスの皮でくるみます。
// gdbm_db.h #ifndef GDBM_DB_H_ #define GDBM_DB_H_ #include <stdexcept> #define GDBM_STATIC #include <gdbm.h> namespace gdbm { class db { public: db(); db(const char* name, int block_size =512, int read_write =GDBM_WRCREAT, int mode =0644); ~db(); bool open(const char* name, int block_size =512, int read_write =GDBM_WRCREAT, int mode =0644); bool is_open() const; void close(); int store(datum key, datum content, int flag =GDBM_REPLACE); datum fetch(datum key) const; bool exists(datum key) const; int remove(datum key); // gdbm_delete datum firstkey() const; datum nextkey(datum key) const; int reorganize(); void sync(); static const char* strerror(gdbm_error err); int setopt(int option, int* value, int size); int fdesc(); private: db(const db&); // no impl. to avoid copy db& operator=(const db&); // no impl. to avoid copy static void raise_error(const char* message); GDBM_FILE dbf_; }; class db_error : public std::runtime_error { public: db_error(const char* message) : std::runtime_error(message) {} }; } #endif
// gdbm_db.cpp #include "gdbm_db.h" namespace gdbm { db::db() : dbf_(nullptr) {} db::db(const char* name, int block_size, int read_write, int mode) { open(name, block_size, read_write, mode); } db::~db() { close(); } bool db::open(const char* name, int block_size, int read_write, int mode) { dbf_ = gdbm_open(const_cast<char*>(name), block_size, read_write, mode, reinterpret_cast<void(*)()>(&db::raise_error)); return is_open(); } bool db::is_open() const { return dbf_ != nullptr; } void db::close() { gdbm_close(dbf_); dbf_ = nullptr; } int db::store(datum key, datum content, int flag) { return gdbm_store(dbf_, key, content, flag); } datum db::fetch(datum key) const { return gdbm_fetch(dbf_, key); } int db::remove(datum key) { return gdbm_delete(dbf_, key); } bool db::exists(datum key) const { return gdbm_exists(dbf_, key) != 0; } datum db::firstkey() const { return gdbm_firstkey(dbf_); } datum db::nextkey(datum key) const { return gdbm_nextkey(dbf_, key); } int db::reorganize() { return gdbm_reorganize(dbf_); } void db::sync() { return gdbm_sync(dbf_); } const char* db::strerror(gdbm_error err) { return gdbm_strerror(err); } int db::setopt(int option, int* value, int size) { return gdbm_setopt(dbf_, option, value, size); } int db::fdesc() { return gdbm_fdesc(dbf_); } void db::raise_error(const char* message) { throw db_error(message); } }
中のGDBMがスケて見えるほどの薄いwrapperですが、よく使われる定数値をデフォルト引数とすることで、利用者側は多少なりとも楽ができます。
#include <iostream> #include <cassert> #include <array> #include <cstring> // strlen #include <cstdlib> // free #include "gdbm_db.h" using namespace std; void store() { gdbm::db dbx("trial.db", 512, GDBM_NEWDB); assert( dbx.is_open() ); 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 ) { 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; int result = dbx.store(key, content); assert( result == 0 ); } cout << "3 entries stored." << endl; } void fetch() { gdbm::db dbx("trial.db", 512, GDBM_WRCREAT); assert( dbx.is_open() ); array<const char*,4> keys = { "apple", "orange", "grape", "peach" }; datum key; datum content; for ( const char* target : keys ) { key.dptr = const_cast<char*>(target); key.dsize = strlen(target) + 1; content =dbx.fetch(key); if ( content.dptr == nullptr ) { cout << target << " not found..." << endl; } else { cout << target << " = " << content.dptr << endl; free(content.dptr); } } cout << "--- list all entries ---" << endl; key = dbx.firstkey(); while ( key.dptr != nullptr ) { char* tmp = key.dptr; content = dbx.fetch(key); cout << key.dptr << " = " << content.dptr << endl; free(content.dptr); key = dbx.nextkey(key); free(tmp); } } int main() try { store(); fetch(); } catch ( gdbm::db_error& ex ) { cout << ex.what() << endl; }