SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

GDBMであそんでみたよ

薄いwrapperから連想配列まで

  • X ポスト
  • このエントリーをはてなブックマークに追加

std::stringインターフェース

 も一つイケてないのは、datumとstd::stringとの変換でしょうか。GDBMとのキー/値の受け渡しはいずれもdatumを介して行うために、登録/検索のたびにdatumとstd::stringとの変換を要します。ここんとこを簡略化すべく、変換関数to_datum()とfrom_datum()を用意しましょう。

list-08
// gdbm_db.h(部分)
std::string from_datum(const datum& d);
datum       to_datum(const std::string& s);
list-09
std::string from_datum(const datum& d) {
  std::string result;
  if ( d.dptr != nullptr ) result.assign(d.dptr, d.dsize);
  return result;
}

datum to_datum(const std::string& s) {
  datum result;
  result.dptr = const_cast<char*>(s.data());
  result.dsize = s.size();
  return result;
}

 これで利用者側コードはいくぶん涼しくなりますね。

list-10
#include <iostream>
#include <cassert>
#include <array>

#include "gdbm_db.h"

using namespace std;

void store() {
  gdbm::db dbx("trial.db", 512, GDBM_NEWDB);
  assert( dbx.is_open() );

  array<string,4> keys     = { "apple",  "orange", "grape",  "apple" };
  array<string,4> contents = { "まっく", "みかん", "ぶどう", "りんご" };

  for ( size_t i = 0; i < keys.size(); ++i ) {
    int result = dbx.store(gdbm::to_datum(keys[i]), gdbm::to_datum(contents[i]));
    assert( result == 0 );
  }
  cout << "3 entries stored." << endl;
}

void fetch() {
  gdbm::db dbx("trial.db", 512, GDBM_WRCREAT);
  assert( dbx.is_open() );

  array<string,4> keys     = { "apple",  "orange", "grape",  "peach" };

  for ( const string& target : keys ) {
    datum key = gdbm::to_datum(target);
    gdbm::auto_datum content =dbx.fetch(key);
    if ( empty(content) ) {
      cout << target << " not found..." << endl;
    } else {
      cout << target << " = " << gdbm::from_datum(content) << endl;
    }  
  }
  cout << "--- list all entries ---" << endl;
  gdbm::auto_datum key = dbx.firstkey();
  while ( !empty(key) ) {
    gdbm::auto_datum content = dbx.fetch(key);
    cout << gdbm::from_datum(key) << " = " << gdbm::from_datum(content) << endl;
    key = dbx.nextkey(key);
  }
}

int main() try {
  store();
  fetch();
} catch ( gdbm::db_error& ex ) {
  cout << ex.what() << endl;
}

連想配列

 これが最後のステップ、さきほどの変換関数をクラス・テンプレート:datum_converterに仕立て、さらにこのdatum_converterをデフォルト・テンプレート引数とするdb_map<K,V>を作ります(K:キー/V:値)。

list-11
// gdbm_db.h(部分)
namespace gdbm {
  template<typename K, typename V, typename KC =datum_converter<K>, typename VC =datum_converter<V>>
  class db_map {
  public:
    db_map(db& d, KC kc=KC(), VC vc=VC()) : db_(d), kc_(kc), vc_(vc) {}
    int store(const K& key, const V& val, int flag =GDBM_REPLACE) { return db_.store(kc_.to_datum(key), vc_.to_datum(val), flag); }
    bool fetch(const K& key, V& val) {
      auto_datum d = db_.fetch(kc_.to_datum(key));
      if ( !empty(d) ) { val = vc_.from_datum(d); } 
      return !empty(d); 
    }
    bool firstkey(K& key) { 
      auto_datum d = db_.firstkey(); 
      if ( !empty(d) ) { key = kc_.from_datum(d); } 
      return !empty(d); 
    }
    bool nextkey(K& key) { 
      auto_datum d = db_.nextkey(kc_.to_datum(key)); 
      if ( !empty(d) ) { key = kc_.from_datum(d); } 
      return !empty(d); 
    }
    class db_map_ref {
    public:
      db_map_ref(db_map& db, const K& key) : db_(db), key_(key) {}
      operator V() { V result; db_.fetch(key_,result); return result; }
      db_map_ref& operator=(const V& val) { db_.store(key_,val); return *this; }
    private:
      db_map& db_;
      const K& key_;
    };
    db_map_ref operator[](const K& key) { return db_map_ref(*this,key); }
  private:
    db& db_;
    KC kc_;
    VC vc_;
  };
}

 db_map<K,V>は連想配列をサポートしています。db_map<K,V>は内部クラスdb_map_refを返すoperator[](const K&) をメンバ関数に持っています。db_map_refはそれが右辺にくれば(operator V()によって)キーに紐づいた値を返し、左辺にくれば(operator =(const V&)によって)その右辺値をVとしてgdbmにstore()します。そんなわけで利用者コードはデータベースを配列であるかのように扱えます。

list-12
#include <iostream>
#include <cassert>
#include <array>

#include "gdbm_db.h"

using namespace std;

void store() {
  gdbm::db dbx("trial.db", 512, GDBM_NEWDB);
  assert( dbx.is_open() );
  gdbm::db_map<string,string> dmap(dbx);

  array<string,4> keys     = { "apple",  "orange", "grape",  "apple" };
  array<string,4> contents = { "まっく", "みかん", "ぶどう", "りんご" };

  for ( size_t i = 0; i < keys.size(); ++i ) {
    dmap[keys[i]] = contents[i];
  }
  
  cout << "3 entries stored." << endl;
}

void fetch() {
  gdbm::db dbx("trial.db", 512, GDBM_WRCREAT);
  assert( dbx.is_open() );
  gdbm::db_map<string,string> dmap(dbx);

  array<string,4> keys     = { "apple",  "orange", "grape",  "peach" };

  for ( const string& key : keys ) {
    string content = dmap[key];
    if ( !content.empty() ) {
      cout << key << " = " << content << endl;
    } else {
      cout << key << " not found..." << endl;
    }  
  }

  cout << "--- list all entries ---" << endl;
  string key;
  string content;
  bool hasmore = dmap.firstkey(key);
  while (  hasmore ) {
    dmap.fetch(key, content);
    cout << key << " = " << content << endl;
    hasmore = dmap.nextkey(key);
  }

}

int main() try {
  store();
  fetch();
} catch ( gdbm::db_error& ex ) {
  cout << ex.what() << endl;
}

 以上、C++の基本的な構文とテクニック(と呼べるほどのものではないけれど)を使ってC-interfaceから連想配列に仕立てるまでのオハナシでした。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7216 2013/06/26 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング