Notes C API探訪: テキスト用LIST型のラップクラス(その5)
前回の記事からの続きで、ListDuplicate関数を使ってコピーに関するメソッドを作ってみます。ListDuplicate関数については、こちらの記事でご紹介しています。
nxpp::TextListクラスのテンプレート化
では、実際にコピーの実装に入る前に、まずnxpp::TextListクラスで見落としていたデータタイププレフィックス設定についての修正をしていきます。
これまで、データタイププレフィックスについては、メモリサイズに直結するので、その有無についてはきちんと実装してきました。ただし、データタイププレフィックスの領域(WORD型)に収まる値そのものを設定してきませんでした(実は、ListAllocate関数でデータタイププレフィックスを「有り」とすると、データタイプを「TYPE_TEXT_LIST(1281)」に設定してくれます)。
これをテンプレート変数として供給するようにします。
// nxpp/include/nxpp_textlist.hpp nxpp名前空間内
template <WORD Type = TYPE_TEXT_LIST> // <= テンプレート化!
class TextList
{
// ...
};
// 使用する場合、
nxpp::TextList<TYPE_TEXT_LIST> textList;
// または単に、
nxpp::TextList<> textList;
これまでの記事でも述べているように、LIST構造体で扱えるデータ要素はテキストだけではないので、今後別のデータ型を当てはめる可能性は0ではないことも考慮して、変更できる余地を残しておきます。ただし、デフォルトはテキストリスト(TYPE_TEXT_LIST)としておきます。
allocateメソッドの修正
この修正により、既存のコードではallocateメソッドが、次のように変更になります。
// nxpp/include/nxpp_textlist.hpp class nxpp::TextList内
private:
/**
* @brief LIST領域の確保
* @param count 初期要素数
* @param quota 初期割当サイズ
* @return 成功すればTRUE
*/
bool allocate(WORD count = 0, WORD quota = 0) {
WORD *ptr = nullptr; // <= Here!
WORD size = 0;
Status status = ListAllocate(
count,
quota,
fPrefix_,
&handle_,
&ptr,
&size
);
if (!status) { throw status; }
if (fPrefix_ && *ptr != Type) { *ptr = Type; } // <= Here!
OSUnlockObject(handle_);
return true;
}
ListAllocate関数が返してくるロック済みのポインタをWORDポインタとして受け取り、プレフィックスが「有り」でテンプレート引数値と内容が違えば、データタイプをテンプレート引数の値で更新します。
データタイププレフィックスとデータタイプの取得
また、次のようなデータタイププレフィックスにまつわるメソッドも追加します。
// nxpp/include/nxpp_textlist.hpp class nxpp::TextList内
public:
/**
* @return データタイププレフィックスの有無
*/
BOOL fPrefix() const { return fPrefix_; }
/**
* @return データタイプ
*/
WORD type() const {
ObjectLocker<LIST> locker(handle_);
return !locker ? 0 : type(&locker, fPrefix_);
}
/**
* @param pList LISTへのポインタ
* @param fPrefix データタイププレフィックス
* @return データタイプ
*/
static WORD type(LIST *pList, BOOL fPrefix) {
return fPrefix ? *reinterpret_cast<WORD*>(pList) : TYPE_INVALID_OR_UNKNOWN;
}
データタイプはテンプレート引数で供給されますが、typeメソッドはあくまで領域内のWORD値を返します。プレフィックスが「無し」ならば無効値を返します。
LISTポインタから直接コピー
仕様変更パートが長くなりましたが、ここで本題に入ります。
まず、最も実装が簡単な、LISTポインタとデータタイププレフィックスの有無から、内容をコピーするコンストラクタです。
// nxpp/include/nxpp_textlist.hpp class nxpp::TextList内
public:
/**
* @brief コンストラクタ
* @param pList LISTポインタ
* @param fPrefix データタイププレフィックスの有無
*/
TextList(LIST *pList, BOOL fPrefix = FALSE)
: handle_(NULLHANDLE)
, fPrefix_(fPrefix)
{
ListDuplicate(pList, fPrefix_, &handle_);
}
この場合、単にListDuplicate関数を呼び出すだけです。コピーされたデータのハンドルはそのままこのインスタンスのハンドルになります。この関数はSTATUS型を返しますが、失敗したところでNULLHANDLEにしておけばいいので、特別後処理も用意しません。
コピーコンストラクタによるコピー
次に、コピーコンストラクタを実装してみます。
// nxpp/include/nxpp_textlist.hpp class nxpp::TextList内
public:
/**
* @brief コピーコンストラクタ
* @param other コピー元
*/
TextList(const TextList &other)
: handle_(NULLHANDLE)
, fPrefix_(other.fPrefix_)
{
ObjectLocker<LIST> locker(other.handle_);
if (!locker) return;
ListDuplicate(&locker, fPrefix_, &handle_);
}
コピー元となるotherのハンドルをロックしてポインタを得て、ListDuplicate関数で複製したテキストリストのハンドルを自身のものとします。
代入演算子によるコピー
最後に、少し面倒な代入演算子のオーバーロードです。
// nxpp/include/nxpp_textlist.hpp class nxpp::TextList内
public:
/**
* @brief 代入演算子
* @param other 代入元
* @return このオブジェクトの参照
*/
TextList &operator =(const TextList &other) {
if (&other != this) {
ObjectLocker<LIST> locker(other.handle_);
if (!!locker) {
if (handle_ != NULLHANDLE) {
OSMemFree(handle_);
handle_ = NULLHANDLE;
}
fPrefix_ = other.fPrefix_;
ListDuplicate(&locker, fPrefix_, &handle_);
}
}
return *this;
}
代入演算子の場合、自身が保持しているハンドルが有効だった場合、一度破棄する必要があります。ハンドルが持つメモリを解放した後、ListDuplicate関数で内容がコピーされたハンドルを保持します。
まとめ
次回から、LIST構造体と同じように複数の要素を扱う構造体「RANGE」をラップしていきます。RANGE型のラップについては、記事「RANGE(データ型)その1」〜「同その9」ですでに紹介済みですが、これらはテンプレート関数での実装でした。今回は、テンプレートクラスのクラスメソッドとして実装し直し、インスタンスメソッドも追加してより扱いやすいようにしてみたいと思います。