template library を作る - 組み込み関数ラッパ

since: 2001-12-?? update: 2001-12-?? count:

コード - builtinfuncs.h

#include <functional>
template <class _New>
class New {
public:
    _New* operator () () {
    return new _New;
    }
};

template <class _New, class _From>
class NewCopy : public std::unary_function<_From, _New*> {
public:
    _New* operator () (const _From& from) {
    return new _New(from);
    }
};

// VC などテンプレート特別バージョンをサポートしてない
// コンパイラだと動きません
#ifndef __DONT_HAVE_TEMP_CLASS_SPEC__
template <class _New, class _From>
class NewCopy<_New, _From*>
    : public std::unary_function<_From*, _New*> {
public:
    _New* operator () (const _From* from) {
    return new _New(*from);
    }
};
#else
template <class _New, class _From>
class NewCopyFromPtr :
    public std::unary_function<_From, _New*> {
public:
    _New* operator () (const _From from) {
    return new _New(*from);
    }
};
#endif // ! __TEMP_CLASS_SPEC__

template <class _Target>
class Delete : public std::unary_function<_Target, void> {
public:
    void operator () (_Target*& target) {
    delete target;
    target = 0;
    }
};

template <class _Casted, class _From>
class DynamicCast : public std::unary_function<_From, _Casted> {
public:
    _Casted operator () (_From from) {
    return dynamic_cast<_Casted>(from);
    }
};

template <class _Casted, class _From>
class StaticCast : public std::unary_function<_From, _Casted> {
public:
    _Casted operator () (_From from) {
    return static_cast<_Casted>(from);
    }
};

template <class _Casted, class _From>
class ReinterpretCast : public std::unary_function<_From, _Casted> {
public:
    _Casted operator () (_From from) {
    return reinterpret_cast<_Casted>(from);
    }
};

template <class _Casted, class _From>
class ConstCast : public std::unary_function<_From, _Casted> {
public:
    _Casted operator () (_From from) {
    return const_cast_Casted>(from);
    }
};

#include <algorithm>

/// STL コンパチなコンテナの内容を全て解放し、空にする
template <class _Container>
void deleteClear(_Container& cont) {
    for (typename _Container::iterator ite = cont.begin();
     ite != cont.end(); ite++) {
    delete *ite;
    }
    cont.clear();
}

/// 関数オブジェクト版
template <class _Container>
class DeleteClear : public std::unary_function<_Container, void> {
public:
    void operator () (_Container& cont) {
    deleteClear(cont);
    }
};

使い方 - builtinfuncsTest.cc

#include <algorithm>
#include <functional>
#include <vector>
#include <cassert>

/// delete された回数を数える int ラッパ
class DeleteCountInt {
public:
    DeleteCountInt(int val) : _val(val) {}
    operator int () { return _val; }
    ~DeleteCountInt() { i++; }
    int _val;
    static int i;
};
int DeleteCountInt::i = 0;

int main() {
    // New and Delete
    {
    std::vector<int*> ints;
    std::vector<DeleteCountInt*> dcis;

    ints.resize(2);
    std::generate(ints.begin(), ints.end(), New<int>());
    deleteClear(ints);

    ints.push_back(new int(1));
    ints.push_back(new int(2));

    std::transform(ints.begin(), ints.end(), std::back_inserter(dcis),
               NewCopy<DeleteCountInt, int*>());

    assert(1 == *dcis.front());
    assert(2 == *dcis.back());

    deleteClear(ints);

    std::for_each(dcis.begin(), dcis.end(), Delete<DeleteCountInt>());
    assert(DeleteCountInt::i == 2);
    // delete した後 0 をセットしているので OK
    std::for_each(dcis.begin(), dcis.end(), Delete<DeleteCountInt>());
    assert(DeleteCountInt::i == 2);
    }

    // Casts
    {
    std::vector<const int*> cints;
    std::vector<int*> ints;

    cints.push_back(new int(1));
    cints.push_back(new int(2));

    std::transform(cints.begin(), cints.end(), std::back_inserter(ints),
               ConstCast<int*, const int*>());

    deleteClear(ints);
    }

    return 0;
}

説明

c にない c++ の予約語で関数のように機能するものは new, delete, typeid, dynamic_cast, static_cast, reinterpret_cast, const_cast とこれだけあるのですが、 これらは予約語というだけあって、 関数オブジェクトでないどころか、関数オブジェクトでさえありません。 つまり、このままではアルゴリズムが適用できません。 どうすれば良いか、ラッピングすればよいのです。 ということでこれはラッパの一実装例です。

んで、説明、ですが、こんなもの読んでいる人なら使い方を見れば、 まあ、わかると思います。 ただ、二点ほど注意点があるのでそれについて述べておきます

一つめは、テンプレート特別バージョンが使えない コンパイラが存在する、ということです。 というか、恐らく使用率ダントツトップであろう VC がサポートしてません。 そういう場合の対処ですが、私が今回書いたような、 別の名前を持ったテンプレートクラスを作れば良いと思います。 (NewCopy の特別バージョンが作れなかったので NewCopyPtr を作った) その時は ifdef で切ることを忘れずに。

もう一つはインスタンスを生成して帰すテンプレート関数は 何を引数にするか、という問題です。 基本的にある程度の大きさのクラスはポインタで管理されることが多いです。 その証拠に STL でも std::mem_fun と std::mem_fun_ref として コンテナに入っているものはポインタであるのが標準だとされています。 それなのに、コピーコンストラクタというものは、伝統的に、 const な参照をとることになっています。 そこで問題になるのが、 参照を引数とするコピーコンストラクタにポインタを渡したいときです。 さすがに

return new _New(*from);

と無前提に書いてしまうのでは、 完全にポインタしか受け付けなくなってしまい、汎用性に欠けます。 今回の私の場合では、 私の見る限りポインタを一つとるコンストラクタを持つ、 頻繁にコピーされるクラスというものは非常に珍しいので、 ポインタをテンプレート引数にとった場合は、 テンプレート特別バージョンを用いて、 上記のようなポインタしか受け付けないバージョンを使うことにしました。 しかし、そのせいでインターフェイスに不整合が出てしまっていますし、 正味のところ、最高の解決法とは言えないようです。 さらに言うなら、この問題に対して、最高の解決法というものは 存在しないと思います。 だから、まあ、せいぜい使い方をきっちりとコメントに書くことです。

…ちなみに typeid は gcc ではまともに動かないので 他の環境で作るのが面倒くさくて作っていません。 type_info はコピーを作れないので、IsSameType みたいな名前の、 テンプレート引数で派生クラスと基底クラスの名前を教えておいて、 基底クラスのポインタを渡してそのタイプを判定する、 というようなインターフェイスのものになると思います。

だんだん説明がめんどくさくなってきた。


home / index

全てリンクフリーです。 コード片は自由に使用していただいて構いません。 その他のものはGPL扱いであればあらゆる使用に関して文句は言いません。 なにかあれば下記メールアドレスへ。

shinichiro.hamaji _at_ gmail.com / shinichiro.h