2013年11月17日日曜日

カスタムアロケータで割り当てサイズ制限

"C++のカスタムアロケータを作ってみる"に続いて,寝る前にもうひとまとめ.

カスタムアロケータにmax_sizeを定義して,数値を返すようにすると,一度に割り当てられる要素の数をその数で制限できる.
ということで,適当に4個にして実験してみる.

#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;
    }

    size_type max_size() const noexcept
    {
        return 4;
    }
};

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

5個目を追加しようとした時点で,length_errorの例外が発生する.
あくまでも1度に割り当てられる要素数の最大値であって,全体として割り当てられる要素数の最大値ではない.
具体的には,allocate(N)を実行したときに,N <= max_size()なら割り当てに成功する,という意味だそうな.

ちなみに,このmax_sizeが定義されていない場合はnumeric_limits<size_type>::max()がmax_sizeとして使われる.
特定の関数が定義されていない場合に別の関数が呼び出されるようにするためのテンプレート周りのテクニックについては,どこかで解説されていた気がするけど,忘れた.

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);
}

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

2013年11月15日金曜日

C++11のchronoライブラリで60FPSと30FPSの間のフレーム数変換

元々60FPSだったものを30FPSに作り替えたりする際に,60FPSでNフレームだったのは30FPSだと・・・
とか逆だったりとかをC++11だと,chronoライブラリあるし,使えるかなぁ,と思って試してみた.

#include <iostream>
#include <ratio>
#include <chrono>
#include <cstdint>

using namespace std;
using namespace std::chrono;

int main()
{
    typedef duration<int32_t, ratio<1, 60>> fps60_t;
    typedef duration<int32_t, ratio<1, 30>> fps30_t;

    // 60FPS -> 30FPS
    fps60_t fps60(10);
    fps30_t fps30 = duration_cast<fps30_t>(fps60);

    cout << fps30.count() << endl;

    // 30FPS -> 60FPS
    fps30 = decltype(fps30)(10);
    fps60 = duration_cast<decltype(fps30)>(fps30);

    cout << fps60.count() << endl;
}

下手に暗黙的な変換にしてしまうのは良くないのは分かってるんだけど,
もうちょい暗黙的な変換欲しいなぁ,と言う気が.

2013年11月13日水曜日

Python APIでコマンドラインからJenkinsのジョブビルド実行

コマンドラインからJenkinsのジョブをビルドしたいな,と思っていたところ,
Python APIがあったので試してみた.

インストール

easy_installでjenkinsapiをインストール
> sudo easy_install jenkinsapi

ビルド実行

import jenkinsapi
from jenkinsapi.jenkins import Jenkins
J = Jenkins(Jenkinsサーバのアドレス[, ユーザ名, パスワード])
J.build_job(ジョブ名)

ユーザ名とパスワードはオプションだけど,設定していないってことはあまりないような.

ドキュメントはどこ?

とりあえずやりたいことは出来たものの,ドキュメントが見当たらないので,他に何ができるかいまいち分からない.
公式ドキュメントはあるものの,上記のJenkinsクラスも載っていないようで,Jenkins側のドキュメントで知った.
ジョブのビルド方法もソースコードを読んで調べたのだけれど,Python業界だと何か方法があるのだろうか?