2013年11月16日土曜日

C++のカスタムアロケータを作ってみる

C++のカスタムアロケータの作り方について,いまいち情報が無いので,
自分で作ってみることに.

環境は,MacBookAir,Mavericks,clang 3.4, libc++でC++11環境に.

とりあえず,メモリの割り当てと解放が行われたときにログを吐くアロケータにしてみる.
いきなり必要と思われる要素を詰め込むと理解が追いつかないので,まずは空のクラスから始める.

#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
};

int main()
{
    vector<int, log_allocator> iv;
}

これをビルドすると,色々エラーが出るけど,とりあえずvalue_typeを提供していないのが問題のようなので,value_typeを用意する.

#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
};

int main()
{
    vector<int, log_allocator> iv;
}

今度は,deallocateが無い,と言われる.deallocateのインターフェースは,void deallocate(pointer p, size_type n)とのこと.
pointerはポインタ型へのtypedefで,size_typeはsize_t型へのtypedefのようなので,次のようにしてみる.

#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
    typedef int* pointer;
    typedef size_t size_type;

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
    }
};

int main()
{
    vector<int, log_allocator> iv;
}
これでビルドは通るようになったので,次はvectorに値を追加してみる.
#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
    typedef int* pointer;
    typedef size_t size_type;

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
    }
};

int main()
{
    vector<int, log_allocator> iv;
    iv.push_back(1);
}
今度は,allocateが無いと言われる.allocateのインターフェースは,pointer allocate(size_type n, allocator::const_pointer hint = 0) となっているけれど,ここではとりあえずhintを無視してみる.
#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
    typedef int* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n)
    {
        cout << "allocate " << n << endl;
        return nullptr;
    }

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
    }
};

int main()
{
    vector<int, log_allocator> iv;
    iv.push_back(1);
}
nullptrを返すようにしたのに,問題無く動作してしまう.もう少し値を追加してみる.
#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
    typedef int* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n)
    {
        cout << "allocate " << n << endl;
        return nullptr;
    }

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
    }
};

int main()
{
    vector<int, log_allocator> iv;
    iv.push_back(1);
    iv.push_back(2);
}
もう1つ値を追加すると,セグメンテーションフォールトを起こした. 本来,メモリ割り当てに失敗した場合,bad_allocの例外を投げるので,nullptrが返された場合のチェックはしていないようだ. とりあえず,デフォルトのnewとdeleteを使ってメモリを割り当ててみる.ついでに,もう1つ値を追加するようにしてみる.
#include <iostream>
#include <vector>

using namespace std;

class log_allocator {
public:
    typedef int value_type;
    typedef int* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n)
    {
        pointer p = new int[n];
        cout << "allocate " << p << " : " << n << endl;
        return p;
    }

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
        delete [] p;
    }
};

int main()
{
    vector<int, log_allocator> iv;
    iv.push_back(1);
    iv.push_back(2);
    iv.push_back(3);
}
これで,めでたく動作したわけだけど,今のままだとint型しか受け付けないので,最後にテンプレートにしてみる. テストのために,intからdoubleに切り替えてみる.
#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class log_allocator {
public:
    typedef T value_type;
    typedef T* pointer;
    typedef size_t size_type;

    pointer allocate(size_type n)
    {
        pointer p = new T[n];
        cout << "allocate " << p << " : " << n << endl;
        return p;
    }

    void deallocate(pointer p, size_type n)
    {
        cout << "deallocate " << p << " : " << n << endl;
        delete [] p;
    }
};

int main()
{
    vector<double, log_allocator<double>> iv;
    iv.push_back(1);
    iv.push_back(2);
    iv.push_back(3);
}

とりあえず,これで一段落.本当は,もう少し色々と定義しないといけないことがあるけれど.
毎回差分じゃなく全文を載せたから,無駄に長くなってしまったので,続きはまた今度にしよう.

0 件のコメント:

コメントを投稿