SHOEISHA iD

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

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

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

特集記事

コマンドラインの解析

Boost.program_optionsのご紹介

コンソールアプリの実装

 それでは本番、key-value pairを操作するコンソールアプリ:ezdbの実装です。とは言ってもやることは単純、挿入(store)/検索(fetch)/一覧(list)およびDB作成(create,new)のいずれかのコマンドをオプションスイッチで指定し、コマンドに応じて--key/--valueでキーと値を指定します。長々と説明するよりコードを見てもらったほうがよさそう。gdbmの操作は、前回作ったwrapperを再利用しました。

list05
#include <iostream>
#include <string>
#include <vector>

#include <boost/program_options.hpp>
#include <boost/optional.hpp>

#include "gdbm_db.h"

namespace po = boost::program_options;
using namespace std;

int main(int argc, char* argv[]) {
  try {
    po::options_description visible("Allowed options");
    visible.add_options()
      ("help,h"      , "produce help message")
      ("store,s"     , "store key-value pair")
      ("fetch,f"     , "fetch key")
      ("list,l"      , "list all key-value pairs")
      ("create,c"    , "create db (do nothing if exist)")
      ("new,n"       , "create new db (even if exist)")
      ("block_size,b", po::value<int>()->value_name("<N>")->default_value(512), "block size on creation")
      ("key,k"       , po::value<string>()->value_name("<key>"), "key")
      ("value,v"     , po::value<string>()->value_name("<value>"), "value (default on fetch)")
     ;

    po::options_description hidden("Hidden options");
    hidden.add_options()
      ("gdbm_file", po::value<string>(), "gdbm_file")
     ;

    po::options_description desc;
    desc.add(visible).add(hidden);

    po::positional_options_description pos;
    pos.add("gdbm_file",1);

    po::command_line_parser parser(argc,argv);
    parser.style(po::command_line_style::default_style | po::command_line_style::allow_slash_for_short);
    parser.options(desc).positional(pos);

    po::variables_map vm;
    po::store(parser.run(), vm);
    po::notify(vm);    

    // ここまで↑がコマンドライン解析 ここから↓が本体

    typedef boost::optional<po::variable_value> option_t;
    auto get = [&vm](const string& name) {
      return vm.count(name) ? option_t(vm[name]) : option_t();
    };

    if ( get("help") ) {
      cout << "ezdb <option>... gdbm_file\n" << visible << endl;
      return 0;
    }

    if ( !get("gdbm_file") ) {
      cout << "<gdbm_file> missing." << endl;
      return 1;
    }

    string   gdbm_file  = get("gdbm_file")->as<string>();
    int      block_size = get("block_size")->as<int>();
    option_t key_opt    = get("key");
    option_t value_opt  = get("value");

    int open_mode;
    int count = 0;
    if ( get("store")  ) { open_mode = GDBM_WRITER;  ++count; }
    if ( get("fetch")  ) { open_mode = GDBM_READER;  ++count; }
    if ( get("list")   ) { open_mode = GDBM_READER;  ++count; }
    if ( get("create") ) { open_mode = GDBM_WRCREAT; ++count; }
    if ( get("new")    ) { open_mode = GDBM_NEWDB;   ++count; }
    if ( count != 1 ) {
      cout << "one, and only one of store/fetch/list/create/new needs to be chosen." << endl;
      return 1;
    }

    gdbm::db db(gdbm_file, block_size, open_mode);

    if ( !db.is_open() ) {
      cout << gdbm_file << " open failure." << endl;
      return 1;
    }

    auto to_datum   = [](const string& s) { return gdbm::datum_converter<string>::to_datum(s);   };
    auto from_datum = [](const datum&  d) { return gdbm::datum_converter<string>::from_datum(d); };

    if ( get("store") ) {
      if ( !key_opt ) {
	    cout << "<key> should be specified." << endl;
	    return 1;
	  }
      if ( !value_opt ) {
	    cout << "<value> should be specified." << endl;
	    return 1;
	  }
      db.store(to_datum(key_opt->as<string>()), to_datum(value_opt->as<string>()));
    }

    if ( get("fetch") ) {
      if ( !key_opt ) {
        cout << "<key> should be specified." << endl;
        return 1;
      }
      gdbm::auto_datum value = db.fetch(to_datum(key_opt->as<string>()));
      if ( value.dptr == nullptr ) {
        if ( value_opt ) cout << value_opt->as<string>() << endl;
      } else {
        cout << from_datum(value) << endl;
      }
    }

    if ( get("list") ) {
      for ( gdbm::auto_datum key = db.firstkey(); !empty(key); key = db.nextkey(key) ) {
        gdbm::auto_datum content = db.fetch(key);
        cout << from_datum(key) << " = " << from_datum(content) << endl;
      }
    }

    return 0;

  } catch(exception& e) {
    cerr << "error: " << e.what() << "\n";
  } catch(...) {
    cerr << "Exception of unknown type!\n";
  }
  return 1;
}

 program_optionsを使ったコマンドライン解析のご紹介でした。さすがはBoost、よくできています。コレ使わずに力技でコマンドライン解析やったら、それだけで数百行書くハメになったんじゃないかしら。

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

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

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

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/7243 2013/07/16 14:00

イベント

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

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

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

メールバックナンバー