見出し画像

Notes C API探訪: データベースハンドルをラップする

以前紹介した記事で、データベースまたはディレクトリを開くとハンドルを得られる話をしました。ハンドルの状態では扱いが大変なので、クラスでラップしてみます。

// nxpp/include
#ifndef NXPP_DATABASE_HPP
#define NXPP_DATABASE_HPP

#include "./nxpp_pathnet.hpp"
#include <iostream>

#ifdef NT
#pragma pack(push, 1)
#endif

#include <global.h>
#include <nsfdb.h>

#ifdef NT
#pragma pack(pop)
#endif

namespace nxpp {

class Database;
using DatabasePtr = std::shared_ptr<Database>;
using Db = Database;
using DbPtr = DatabasePtr;

/**
* @brief データベース/ディレクトリクラス
*/
class Database
{
 DBHANDLE handle_; // ハンドル

public:
 /**
  * @brief デフォルトコンストラクタ
  */
 Database() : handle_(NULLHANDLE) {}

 /**
  * @brief コンストラクタ
  * @param handle ハンドル
  */
 Database(DBHANDLE handle) : handle_(handle) {}

 /**
  * @brief デストラクタ
  */
 virtual ~Database() {
   Status status = close();
   if (!status) {
     std::wcerr << fromLmbcs(fromStatus(status)) << std::endl;
   }
 }

 /**
  * @brief ハンドルをクローズする
  * @return 処理結果ステータス
  */
 Status close() {
   Status status = Database::close(handle_);
   handle_ = NULLHANDLE;
   return status;
 }

 /**
  * @return ディレクトリかどうか
  */
 bool isDirectory() const {
   return Database::isDirectory(handle_);
 }

 /**
  * @brief データベース/ディレクトリをオープンする
  * @param pathnet パスネット
  * @return データベース共有ポインタ
  */
 static DbPtr open(const PathNet &pathnet) {
   DBHANDLE handle = NULLHANDLE;
   Status status = NSFDbOpen(pathnet.data(), &handle);
   if (!status) { throw status; }
   return std::make_shared<Database>(handle);
 }

 /**
  * @brief データベース/ディレクトリをオープンする(Lmbcs版)
  * @param path パス
  * @param server サーバー名
  * @param port ポート名
  * @return データベース共有ポインタ
  */
 static DbPtr open(
     const Lmbcs &path,
     const Lmbcs &server,
     const Lmbcs &port = Lmbcs()
     ) {
   return open(PathNet::construct(path, server, port));
 }

 /**
  * @brief データベース/ディレクトリをオープンする(std::wstring版)
  * @param path パス
  * @param server サーバー名
  * @param port ポート名
  * @return データベース共有ポインタ
  */
 static DbPtr open(
     const std::wstring &path,
     const std::wstring &server,
     const std::wstring &port = std::wstring()
     ) {
   return open(toLmbcs(path), toLmbcs(server), toLmbcs(port));
 }

 /**
  * @brief ハンドルを直接クローズする
  * @param hDB データベースハンドル
  * @return 処理結果ステータス
  */
 static Status close(DBHANDLE hDB) {
   if (hDB != NULLHANDLE) {
     return NSFDbClose(hDB);
   }
   return Status();
 }

 /**
  * @param hDB データベースハンドル
  * @return ディレクトリかどうか
  */
 static bool isDirectory(DBHANDLE hDB) {
   USHORT mode = DB_LOADED;
   Status status = NSFDbModeGet(hDB, &mode);
   if (!status) { throw status; }
   return mode == DB_DIRECTORY;
 }
};

} // namespace nxpp

#endif // NXPP_DATABASE_HPP

nxpp::Databaseはnxpp::Db、std::shared_ptr<Database>はnxpp::DatabasePtr、さらにnxpp::DbPtrと省略できます。

データベース/ディレクトリをオープンする時は、クラスメソッドのopenを使います。

nxpp::DbPtr dbPtr = nxpp::Db::open("names.nsf", ""); // クライアントの場合、ローカルの個人アドレス帳を取得

openには、nxpp::PathNet版、nxpp::Lmbcs3つ版、std::wstring3つ版の3つを用意しています。3つ引数を渡す場合は、パス、サーバー名、ポート名の順で渡し、ポート名は省略可能です。

生成したDatabaseオブジェクトがディレクトリを対象にしているかどうかは、isDirectoryメソッドで判定できます。

if (dbPtr->isDirectory()) {
  // ディレクトリの場合
}

openが返す型はDatabaseの共有スマートポインタなので、所有者が0になると自動でクローズされます。

まとめ

nxpp::Databaseクラスのメソッドそのものはまだ全然ありませんが、今後サンプルアプリケーションの進捗とともに増築していきます。

データベース/ディレクトリのハンドルを保持するのがDatabaseクラスの目的ですが、いったん保持してしまうと、オブジェクトを破棄する時にハンドルをクローズしてしまいます。普段はそれで便利なんですが、アドインのような、Notes/Domino本体からハンドルが供給される場合、クローズしてはいけない場面も意外と多いです。そのため、オブジェクトで使えるオブジェクトメソッドと、外部からハンドルを渡すクラスメソッドと、同名メソッドを2つ揃えるようにしています。生成時に「このオブジェクトはクローズしてはいけない」フラグを渡す方法も考えられますが、現状はこの回りくどいし面倒な方法でいこうと思います。

いいなと思ったら応援しよう!