見出し画像

Notes C API探訪: RANGE(データ型)その2

前々回の記事で紹介したRANGE型には、LIST型に比べて操作できる関数が皆無です。しかし、前回紹介したメモリハンドル用の関数を用いて、ヘルパー関数を作成すれば、LIST型と似たようなことができるはずです。

特性構造体

RANGE型は扱うデータ型がNUMBER型とTIMEDATE型に分かれます。それぞれの関数を作成せずに、C++のテンプレート機能を用いて、コーディングを削減します。
NUMBER型か、TIMEDATE型かを表すために特性を表す構造体を作成します。

/**
* @brief NUMBER範囲用特性構造体
*/
struct NumberRangeTraits {
 typedef NUMBER item; ///< 単数データ型定義
 typedef NUMBER_PAIR pair; ///< ペアデータ型定義
};

/**
* @brief TIMEDATE範囲用特性構造体
*/
struct TimeDateRangeTraits {
 typedef TIMEDATE item; ///< 単数データ型定義
 typedef TIMEDATE_PAIR pair; ///< ペアデータ型定義
};

NumberRangeTraitsは、数値範囲用の特性構造体です。
TimeDateRangeTraitsは、日時範囲用の特性構造体です。
それぞれのitemは、RANGE.ListEntriesで扱うリスト要素の型です。
それぞれのpairは、RANGE.RangeEntriesで扱うリスト要素(ペア)の型です。
型の名前を共通化して、テンプレートパラメータとして扱えるようにします。

RANGE型のメモリハンドルとロック済みポインタを取得する

LIST型にはListAllocate関数があり、指定したサイズでメモリハンドルとロック済みポンタを取得できます。
以下のRangeAllocateテンプレート関数は、同様に指定したサイズで範囲型のメモリハンドルとロック済みポインタを取得します。

/**
* @brief 範囲用のメモリを確保し、ハンドルとロック済みポインタを返す。
* @tparam Traits 特性構造体
* @param ListEntries アイテムリスト数
* @param RangeEntries ペアリスト数
* @param fPrefixDataType データタイププレフィックスの有無
* @param rethRange ハンドルへのポインタ
* @param retpRange ロック済みメモリポインタへのポインタ
* @param retRangeSize 確保済みメモリサイズへのポインタ
* @return OSMemAllocが返したステータス値
*/
template <typename Traits>
STATUS RangeAllocate(
   WORD ListEntries,
   WORD RangeEntries,
   BOOL fPrefixDataType,
   DHANDLE *rethRange,
   void **retpRange,
   WORD *retRangeSize
   ) {
 // 割り当てるサイズを算出
 DWORD listSize
     = sizeof(WORD) * (fPrefixDataType ? 1 : 0)
     + sizeof(RANGE)
     + sizeof(Traits::item) * ListEntries
     + sizeof(Traits::pair) * RangeEntries;

 // OSMemAllocを実行
 STATUS status = OSMemAlloc(MEM_GROWABLE, listSize, rethRange);

 // エラー処理
 if (ERR(status) != NOERROR) {
   *rethRange = NULLHANDLE;
   *retpRange = nullptr;
   *retRangeSize = 0;
   return status;
 }

 // ハンドルをロックしてポインタを取得
 *retpRange = OSLockObject(*rethRange);
 memset(*retpRange, '\0', listSize);

 // RANGEデータにエントリー数を設定
 RANGE *pRange = reinterpret_cast<RANGE*>(
       reinterpret_cast<WORD*>(*retpRange)
       + (fPrefixDataType ? 1 : 0)
       );
 pRange->ListEntries = ListEntries;
 pRange->RangeEntries = RangeEntries;

 // 算出したサイズも返す。
 *retRangeSize = static_cast<WORD>(listSize);

 return status;
}

・ListEntriesは、Traits::item型のリストがいくつ必要かを指定します。RANGE::ListEntriesの値になります。
・RangeEntriesは、Traits::pair型のリストがいくつ必要かを指定します。RANGE::RangeEntriesの値になります。
・fPrefixDataTypeは、データタイププレフィックスを持つかどうか指定します。これをTRUEと指定すると、全体のサイズがsizeof(WORD)分増えますが、先頭のWORD値は0のままなので、自身でTYPE_NUMBER_RANGEかTYPE_TIME_RANGEかを設定する必要があります。このテンプレート関数を拡張して、同時にプレフィックスを設定することも可能です。
・rethRangeは、確保したメモリ領域へのハンドルを返します。
・retpRangeは、確保したメモリハンドルをロックして、ポインタを返します。ハンドルはロック済みの状態になるので、OSMemFreeで解放する必要がある場合は、必ずOSUnlockObjectを実行します。
・retRangeSizeは、確保したメモリ領域のサイズを返します。
・戻り値は、OSMemAlloc関数が返したステータス値を返します。

まとめ

今回はRANGEデータのメモリ確保をサポートするRangeAllocateテンプレート関数を定義してみました。基本的にListAllocate関数の仕様に近づけたので、先頭のデータタイププレフィックスを設定しませんが、同時設定できた方が使い勝手がいいのは間違いないので、プレフィックスを同時設定できる関数を作成してみるのもよいでしょう。

注意: コードの利用においてチブル・システムズは一切の責任を負いません。自己責任でご利用をお願いいたします。

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