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つ揃えるようにしています。生成時に「このオブジェクトはクローズしてはいけない」フラグを渡す方法も考えられますが、現状はこの回りくどいし面倒な方法でいこうと思います。