薄い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;
}
