見出し画像

Notes C API探訪: テキスト用LIST型のラップクラス(その2)

前回の記事で、nxpp::TextListの外郭を定義しました。今回はこれに少し肉付けをしていきます。

まず、ハンドルをロックするためのクラスを、インナークラスとして定義します。ロック自体はOSLockObjectで行いますが、スコープがはずれた時、自動的にアンロックしてくれると便利なので、事前に定義しておきます。

// class nxpp::TextList内
private:
  /**
  * @brief LIST領域のハンドルをロックしてポインタを取得するクラス
  * スコープをはずれると自動的にハンドルをアンロックする
  */
 class Locker {
   const TextList *parent_;
   LIST *ptr_;

 public:
   /**
    * @brief コンストラクタ
    * @param parent テキストリストへのポインタ
    */
   Locker(const TextList *parent) : parent_(parent), ptr_(nullptr) {
     DHANDLE handle = parent->handle_;
     ptr_ = handle != NULLHANDLE
         ? reinterpret_cast<LIST*>(OSLockObject(handle))
         : nullptr;
   }

   /**
    * @brief デストラクタ
    */
   virtual ~Locker() {
      if (ptr_ != nullptr) {
       OSUnlockObject(parent_->handle_);
     }
   }

   /**
    * @brief 正常にロックされてLISTポインタが有効か
    */
   operator bool() const { return ptr_ != nullptr;}

   /**
    * @brief ロックされたLISTポインタを得る
    * @return LISTポインタ
    */
   LIST *operator &() const { return ptr_; }

   friend class TextList;
 };

インスタンスを生成すると、nxpp::TextListが持つハンドルが有効ならロックしてポインタを取得します。無効ならヌルポインタになります。インスタンスが破棄されたら、ロックされていればアンロックします。外部でポインタの有効/無効を知りたい場合はbool値を使います。ロックしたポインタを取得したい場合は、インスタンスに&を付けます(例: &locker)。なお、このnxpp::TextList::Lockerクラスはプライベートとして定義しています。

次は、テキストリストが持つ要素数を取得してみます。インスタンス用の他に、LISTポインタをじかに渡された時でも使えるように、クラスメソッドも用意します。

// class nxpp::TextList内
public:
  /**
  * @return 現在の要素数
  */
 WORD size() const {
   Locker locker(this);
   return !locker ? 0 : size(&locker, fPrefix_);
 }

  /**
  * @param pList LISTへのポインタ
  * @param fPrefix データタイププレフィックス
  * @return 現在の要素数
  */
 static WORD size(LIST *pList, BOOL fPrefix) {
   return ListGetNumEntries(pList, fPrefix);
 }

APIとしては、ListGetNumEntries関数を使います。インスタンスメソッドではプライベートインナークラスLockerを使ってハンドルをロックし、ポインタが有効な時はクラスメソッドの戻り値を返します。

同様に、LIST構造体と(使用していれば)データタイププレフィックス、要素データ、これらを合計したバイトサイズを取得してみます。これもインスタンス用、クラス用の2つを用意します。

// class nxpp::TextList内
public:
  /**
  * @return 現在の全体のデータサイズ
  */
 WORD byteSize() const {
   Locker locker(this);
   return !locker ? 0 : byteSize(&locker, fPrefix_);
 }

  /**
  * @param pList LISTへのポインタ
  * @param fPrefix データタイププレフィックス
  * @return 現在の全体のデータサイズ
  */
 static WORD byteSize(LIST *pList, BOOL fPrefix) {
   return ListGetSize(pList, fPrefix);
 }

APIとしては、ListGetSize関数を使います。

次は、要素にアクセスするためのメソッドを定義します。

// class nxpp::TextList内
public:
  /**
  * @brief 要素アクセス
  * @param index 要素インデックス
  * @return インデックスに該当する要素(テキスト)
  */
 Lmbcs at(WORD index) const {
   Locker locker(this);
   return !locker ? Lmbcs() : at(index, &locker, fPrefix_);
 }

 /**
  * @brief 要素アクセス(atと同じ)
  * @param index 要素インデックス
  * @return インデックスに該当する要素(テキスト)
  */
 Lmbcs operator [](WORD index) const {
   return at(index);
 }
 
 /**
  * @brief 要素アクセス
  * @param index 要素インデックス
  * @param pList LISTへのポインタ
  * @param fPrefix データタイププレフィックス
  * @return インデックスに該当する要素(テキスト)
  */
 static Lmbcs at(WORD index, LIST *pList, BOOL fPrefix) {
   auto count = size(pList, fPrefix);
   if (count == 0 || index >= count) { return Lmbcs(); } // 2021-10-09 修正
   char *ptr = nullptr;
   WORD len = 0;
   Status status = ListGetText(pList, fPrefix, index, &ptr, &len);
   if (!status) { throw status; }
   return Lmbcs(ptr, len);
 }

APIとしては、ListGetText関数を使います。場所を示すインデックスが範囲外の時は空の文字列を返します。また、atメソッドの他に、インスタンスメソッドとしてoperator []をオーバーロードしているので、配列のようにもアクセスできます(例: textList[3]はtextList.at(3)と同等)。

おまけに先頭要素にアクセスするfrontと、末尾要素にアクセスするbackも定義します。

// class nxpp::TextList内
public:
  /**
  * @return 先頭要素
  */
 Lmbcs front() const {
   Locker locker(this);
   return !locker ? Lmbcs() : front(&locker, fPrefix_);
 }
 
/**
  * @return 末尾要素
  */
 Lmbcs back() const {
   Locker locker(this);
   return !locker ? Lmbcs() : back(&locker, fPrefix_);
 }
 
 /**
  * @return 先頭要素
  * @param pList LISTへのポインタ
  * @param fPrefix データタイププレフィックス
  */
 static Lmbcs front(LIST *pList, BOOL fPrefix) {
   return at(0, pList, fPrefix);
 }
 
 /**
  * @return 末尾要素
  * @param pList LISTへのポインタ
  * @param fPrefix データタイププレフィックス
  */
 static Lmbcs back(LIST *pList, BOOL fPrefix) {
   auto count = size(pList, fPrefix);
   if (count == 0) { return Lmbcs(); }
   return at(count - 1, pList, fPrefix);
 }

まとめ

今回は、要素にアクセスするために直接インデックスを指定しましたが、標準ライブラリのようにイテレータを用意したいところです。

2021-10-09 修正
atメソッドにおいてインデックスが範囲外になるバグを修正しました。

この記事が気に入ったらサポートをしてみませんか?