<<20112013>>

2012-2-29[水] pyukiwiki 0.2.0

月末に作業締め日がある場合、さて閏年でよかったと思うべきか... はどうでもよく。

このサイトに使っている pyukiwiki 共同開発版は一時開発が止まってたけどまた再開していたようで v0.2.0p1 が出てたので更新してみた。

wikiプログラムの更新だけでサイト自体のリニューアルというわけではないけれど。でも微妙な修正をちょこちょこ、どうせspamしか書き込まないのだからcommentは無しにしたり(少し寂)、無理にblog風にすることもないなで昔のtop頁に戻したり。 (もう少しいじってからとか思うもその状態で2週間放置だったり。)

pyukikwikiの更新のほうは、久しぶりすぎて、いろいろ細かいこと忘れていてすごく手間取った。自分の改造やプラグインの事情が記憶の彼方なのは当然として、使用した環境じゃ CRLF だとcgi(perl)が動いてくれないことに中々気づかなかったり(本来なら年始に移行するつもりが)

0.1.9あたりから utf-8 専用板が用意されてるようなので、utf-8 版にしようと思ったのも一手間。頁等のutf8へのコンバータプラグインが用意されているけれど、さすがに 0.1.7では駄目のようで一旦 euc(従来)版 の0.2.0環境に移行する必要があり... コンバートだけだからと compact 版を使ったところ動かなかったり(full版は動いてくれた)

改造|自前のpluginの辻褄合わせは微妙に面倒だけどしかたなし。editは改造しなくてもよくなってたけど、calender2とか改造に依存したサイトになってるので... 入れ替える時面倒なので結局 改造プラグインは名前を変えて対処したり。

スキンは 0.1.7で使ってたのがほぼ無変更で使えたのでそのまま. うち向きの仕様も増えてるっぽいので ほんとは 0.2.0 のをベースに作りなおしたほうがいいのだろうけど。

と、pyukiwiki共同開発サイト 見に行ったら 0.2.0p2 のアナウンスが出てた模様。 compact版はやっぱり配布内容がおかしかった模様. (p2にしようかと思うもダウンロードは3/1予定... とりあえずp1のままで)




2012-6-3[日] LaVieLight に Win8rp をインストール

久しぶりにサブノートPCを持ち歩こうと思いたち、Win8cp を試しに入れて放置しっぱなしだった LaVie Light BL350/D を復活中。 丁度 Win8 Release Preview が公開したてだったので更地にしてインストールすることにした。

マシンのほうは Atom な netbook の範疇だけど、1366x768 ドットある奴なんで一応 Metoro も大丈夫(なので Cp 版インストールできてた。非力な Atom だけど 1.5GHz 2コア(HT x4) だし、メモリは2GBにHDDはSSD120GBに換装済みなので、それなりに使える感じ)

インストール前に、まず、Microsoft アカウント(Live ID)の氏名欄を 英数のみに変更。

Win8 はPCのユーザー設定で MSなクラウドサービスと連動前提の設定と、そうでない(従来タイプの)設定を選べるが、CP 版時に いろいろ試したく 連動のほうを選んだら、Microsoftアカウント情報に登録してある氏名の“名”の文字列でユーザーが作られてしまったのだった。本名を漢字で設定したままだったから c:/users/本名 な漢字フォルダが作られしまい、古いツールを使う機会もあるプログラマとしては腹立たしい状態だったので、今回はアカウント情報のほうを変えておくことで対処。(CP版のときはこの段階でかなり萎えて余り使わず放置)

更地からなので ISOイメージをDVDに焼いて USB-DVD でインストールした。 途中でプロダクトID を聞かれたので、Webインストール版を実行して手順に従って操作している途中で表示されたプロダクトID を用いた。(この方法でよかったのか不明だがとりあえず動いてる)。

当然インストール開始時には以前インストールしたwindowsの情報は引き継がないほうを選んだのだけど、インストール後デスクトップを選んでみれば壁紙無し単色でタスクバーが縦置き状態……これWin8のデフォじゃないよね? CP版で設定していた己の設定ぽいので、アカウントに紐付された情報が一部あるのかも。

とまOS自体は結構すんなりインストールできたと思うけど、メール設定でミスってしまったようで(ExChangeどうこう持ってないのに選択してしまってエラーは出たと思う)、スタート画面でMailを選ぶとタイトルのみ項目のないアプリが立ち上がるだけ…Metroなアプリってどこで諸設定できるのか……よくわからず放置中。
(間抜けてた。メール画面で右サイドメニュー(右下か右上あたりにマウスカーソル持って行ったら出る奴)の"設定" を選べばメールの設定できた。右サイドメニューの5項目の名前かわらないから、てっきり全体で共通のものかと思ってたらメトロアプリごとに内容は変わってて、デスクトップアプリのメニューバーみたいなものだったのね)

その他アプリのインストールについては、CP版より更にスタートボタン破棄の方向が強められてる感じ。 CP版のときは、タスクバーに新規ツールバーとして

C:\users\(ユーザー名)\AppData\Roming\Microsoft\Windows\Start Menu\Programs

を登録すれば簡易なプログラムメニューとして使うことができたけれど、RPではアプリをインストールしても そもそも このフォルダにアプリのショートカット等は作られず、Metoroなスタート画面にドバっとアイコンが現れる状態。元の(start-プログラムメニューでの)フォルダ構成無視なんで一気にスタート画面がゴミ溜めと化す。全くスタート画面を使わなくてすむならそれでもいいけれど、立ち上げ時に必ず来るしね……しかたないので、右クリックで不要なものをマークして一気にピン止めを外した(ちょっと面倒)。

※ 全部が全部じゃなくて一部(susie)は従来のメニューに現れるものもあった(たぶん START-プログラム・メニューへの登録が今のwinの作法に則っていないのかも)

 

インストールしたプログラムでまともに動かなかったのは TortoiseSVN(1.7.7)。 チェックアウトしたりTortoiseSVNのメニューが閉じられるときにクラッシュする。 それだけでなく Exproler(タスクバー) が不安定になって ゴミ箱を空にしようとしたり タスクバーを自動に隠す設定をやめようとしたらハング(即再起動するが)。
Tortoise??? の仕組みからいって、新規OSでいきなり動かなくてもしかたないとは思うが、使えないのはちょっとつらい。

とりあえず TortoiseSVNをアンイストールして再起動で元に戻ったけれど、最初 アンイストール後シャットダウンで終了して電源ボタンで起動しなおしたら OS不安定のままだったので結構あせった。2,3回やっても変わらずで。再起動を選んだところ"更新します"みたいなメッセージでて再起動後ゴミ箱空にしてもハングしなかったので一安心だけど。Win8からの高速なブートや終了のための仕組みの影響なんでしょうかね。

追記: またゴミ箱空にしたらハングするようになった。FireFox(12)も終了するたびにクラッシュレポータが表示される。 どうやら今度は Google日本語入力が原因だった模様。設定をMS IMEに切り替えたらゴミ箱空にできたしFireFoxも正常終了、googleIMEに切替るとまたハング...うーシステム拡張的なものは結構引っかかる感じなのかな。




2012-10-27[土] Win8pro インストール

Win8の1200円のアップグレード版をダウンロードした勢いで(サブ機だし)Win7引き継ぎでインストールしたはいいけれど不安定になって結局更地で再インストール中。(OSのインストールは楽ちんになっても各アプリ側が面倒この上なく)

引き継ぎインストール、最初はそこそこいい感じに移行できたように思ったけれど、使いだすと引き継げてるもの・引き継げてないものがあってチグハグだった。

特にWindowsエクスペリエンス計測の このコンピューターの評価 がエラーで動かなかったときは唖然...
原因は、ユーザー環境変数 tmp, temp 。これらの内容は win7 での値が引き継がれていたが 指していたパス(=Ramdisk)は win8には引き継がれないため パス不正のため不具合に化けた模様。

[追記] Win8 設定完了前に メインでつかってる Win7機がお亡くなりになったため win8に強制移住。HDDが生きてたので被害は少ないけれど、元環境のアプリを起動できないのはやっぱり面倒。

[追記2] iPad mini を購入したら一気に win8 熱が冷めてしまった。片手で持ててそこそこ広いってのは思いのほか重要だった模様。寝ころんでpdf読んで寝落ちするのはある種幸せです。




2012-12-12[水] サイト分割

このサイトのプログラミング以外のページをこっちに移した。いろいろ思惑あれど遅々として進まず、けれど年越す前には何かしとこうと... というか、c++ advent calendar に参戦申込(12/14) しちまってたのでその前に多少なりとも、で。(肝心のネタをどうしよう... )




2012-12-14[金] boost::container で俺俺アロケータ

これは C++ Advent Calender 2012 14日目 の記事です。

C++11やboostをまだロクに使っていない人間なので、雰囲気を掴むために boost1.52 の boost/container フォルダを一寸覗いてみました。 new,delete等メモリーアロケート のコストを無視できないターゲットを扱うことも多いので、俺俺アロケータを渡してみたいってのもあり。 C++11仕様でアロケータが今までより楽にかけるようだし、scoped_allocator_adaptor も何か使えそうですし.

※いまだvc9をおもに使ってるので(vc12も使いますが)、C++11の記述はさけてます.

boost::container

boost::container ですが、C++11で仕様拡張された vector,list,map,set,deque 等のコンテナの機能を C++03コンパイラでもなるべく使えるようにした互換コンテナ群+α です。 C++11 で増えたコンテナ(array, unorderd_map 等)については すでにboostに実装があるためか(?) boost::container には含まれていません。詳しいことは こちら とか他のサイトにあたってください。

将来的にどんどん変わっていく部分も多々あるでしょうが、とりあえず boost_1_52_0/boost/container/ フォルダで wc してみると、41ファイル 27613 行(detailフォルダ含)。 実際には container 外のboostヘッダをいろいろinclude しているので実質はもっと大きいです(map,set(tree)等は boost::intrusive のものを使っているようです)。 (ちなみに boost_1_52_0/boost/ では 8779ファイル 約172万行、やっぱりデカいです)

boost/container/ 直下にあるファイルは

allocator_traits.hpp std::allocator_traits 互換
string.hppstd::string 互換
vector.hppstd::vector 互換
deque.hppstd::deque 互換
list.hppstd::list 互換
map.hppstd::map・std::multimap 互換
set.hppstd::set・std::multiset 互換
scoped_allocator.hppstd::scoped_allocator 互換
scoped_allocator_fwd.hpp(scoped_allocator の最小宣言)
slist.hpp古のslist の拡張版
stable_vector.hpp vector<T*>で要素を別途アロケートのvector
flat_map.hppvector< pair<KEY,VALUE> > 風実装で mapインターフェースを持つコンテナ
flat_set.hpp(上記の std::set 風実装)
container_fwd.hppboost::container
detail/ 詳細実装のファイル群

(ptr_container のような)既存のstdコンテナを継承して拡張(メンバー関数追加)とかではないです。 (といっても boost::intrusive 等 boost内の他のライブラリはよく使われています)

次に、いくつかコンテナについて。

vector

ざっくりメンバー変数の部分だけ抜き出してみて、

template <class Allocator>
class vector_alloc_holder {
    struct members_holder : public Allocator {
        pointer     m_start;
        size_type   m_size;
        size_type   m_capacity;
    }
}
template <class T, class Allocator>
class vector : vector_alloc_holder<Allocator>;

(SGI版派生等)他の実装だと全てポインタで管理していることが多いですが、この実装はsize,capacityを個数で持っています(個人的にはデバッグ時にメンバー変数で見れるので好み)。

Allocatorは、(非static)メンバー変数がない場合に実質 0バイトになるよう struct の継承元になっています。
stlの実装では 多少の差違はあれ Allocator は (非static)メンバー変数になっていることが多いですが、 C++03 の場合 static メンバー変数で持つのも有りだったようで、実際そうなっていたコンパイラもありました。
C++11 では scoped_allocator_adaptor のこともあるし Allocator は (非static)メンバー変数必須のようです。

ということで C++11なら

// 1回 N個 アロケートするだけの Allocator
template<typename T, unsigned N>
class SampleAllocator1 {
public:
   typedef T value_type;
   SampleAllocator1() { }
   T* allocate(unsigned n) { assert(n == N); return reinterpret_cast<T*>(buf_); }
   void  deallocate(T*, unsigned) { }
   bool operator == (SampleAllocator1 const & ) const { return false; }
   bool operator != (SampleAllocator1 const & ) const { return true; }
private:
   typedef double buf_t;
   buf_t   buf_[(N*sizeof(T)+sizeof(buf_t)-1) / sizeof(buf_t)];
};
//typedef std::vector<int, SampleAllocator1<int,1024> >  Int1024Vector;
typedef boost::container::vector<int, SampleAllocator1<int,1024> >  Int1024Vector;
Int1024Vector  g_intVec;

みたいな真似をしても大丈夫のはず...と書いたけど上記はたぶん boost::container に依存しています.
(ターゲット環境で mallocの準備前に(固定サイズの)コンテナを使いたい...こともあった)

list

実装は boost::intrusive の定義も多く面倒なので、かなり端折って (boost::container としてでなく) ありそうな list 実装のメンバーの雰囲気として以下 (すみません、後付で boost::container からめたはいいけど対処しきれず...)

class Node_Base {
    Node_Base* next_;
    Node_Base* prev_;
};
template<typename T>
class Node : Node_Base {
    T     value_;
};
template<typename T, class A >
class List : A {
    ListNode_Base root_;
    size_t        size_;
};

かなりいい加減ですが、メモリーの雰囲気がわかれば...

map,multimap, set, multiset

map,set は たいてい赤黒木 実装のようで、boost::container では boost::intrusive を使ってるようです。
で、これも メモリーの雰囲気だけ...

class Node_Base {
    Node_Base* left_;
    Node_Base* right_;
    Node_Base* parent_;
    bool       balance_;
};
template<typename T>
class Node : Node_Base {
    T     value_;
};
template<typename T >
class Tree : A {
    Node_Base root_;
    size_t    size_;
};


List や Map は 渡された アロケータをそのまま使うのではなくて、allocator のメンバーの

template <class U> struct rebind { typedef allocator<U> other; };

を用いて、 T でなく Node<T> の allocator を使っています。 また、allocator への要求は通常 1個単位になると思います。

ということで、list や map の俺俺アロケータとしては、ノードサイズ固定のメモリーをプールしておく、というのが考えられます。

template<unsigned B, unsigned N>
class SampleAlloc2 {
public:
    SampleAlloc2() {
        for (unsigned i = 0; i < N-1; ++i)
            buf_[i].link = &buf_[i+1];
        buf_[N-1].link = NULL;
        root_ = &buf_[0];
    }
    void* allocate(unsigned n) {
        assert(n == 1 && root_);
        Link*   p = root_;
        root_ = p->link;
        return p;
    }
    void  deallocate(void* t, unsigned n) {
        if (t) {
            assert(n == 1);
            Link*   p = reinterpret_cast<Link*>(t);
            p->link   = root_;
            root_     = p;
        }
    }
private:
    union Link {
        Link*   link;
        char    b[B];
    };
private:
    Link*       root_;
    Link        buf_[N];
};
enum { ELM_SIZE = 32 }; // コンテナのノードを含んだ要素のバイト数.
enum { MAX_SIZE = 16 };
template<typename T>
class SampleAllocator2 : public SampleAlloc2<ELM_SIZE,MAX_SIZE> {
    typedef SampleAlloc2<ELM_SIZE,MAX_SIZE> base_type;
public:
    typedef T value_type;
    SampleAllocator2() {BOOST_STATIC_ASSERT(sizeof(T) <= ELM_SIZE);}
    T* allocate(unsigned n) { return reinterpret_cast<T*>(base_type::allocate(n)); }
    void  deallocate(T* t, unsigned n) { base_type::deallocate(t, n); }
    bool operator == (SampleAllocator2 const & ) const { return false; }
    bool operator != (SampleAllocator2 const & ) const { return true; }
};
// list
typedef SampleAllocator2<int>  IntAllocator;
typedef boost::container::list<int, IntAllocator > IntList;
// map
struct cstr_less {
    bool operator()(const char* l, const char* r) const { return strcmp(l,r) < 0; }
};
typedef std::pair<char const* const,int> smpPair_t;
typedef SampleAllocator2<smpPair_t> SmpPairAllocator;
typedef boost::container::map<const char*, int, cstr_less, SmpPairAllocator >	StrIntMap;

※ 要素(ノード)サイズ(ELM_SIZE)が直うちでみっともないですが、とりあえずは。

deque

(実装面倒なのと、ターゲットであまり使わないし... いえ、時間切れ. 後日に何か)

string

std::stringは(C++03では)ライブラリごとに結構実装がばらけています(EffectiveSTL に載ってるのとか)。 c++11 で縛りがきつくなったので多少にかよってくるかもですが、それでもいろいろできそうです。

最小限としては vector と同様の内容になりそうですが... boost::container::string から端折って抜き出してみると

 struct long_t {
    size_type      is_short  : 1;
    size_type      length    : (sizeof(size_type)*CHAR_BIT - 1);
    size_type      storage;
    pointer        start;
 };
 struct short_header {
    unsigned char  is_short  : 1;
    unsigned char  length    : (CHAR_BIT - 1);
 };
 struct short_t {
    short_header   h;
    value_type     data[UnalignedFinalInternalBufferChars]; 
                   // UnalignedFinalInternalBufferChars ≒ (sizeof(long_t)-sizeof(short_header))/sizeof(value_type)
 };
 union repr_t {
    long_raw_t  r;
    short_t     s;
 };

long_t は長い文字列でアロケータを用いる場合で、sizeof(long_t)-α 以内におさまる短い場合は short_t のようにして領域をバッファとして使い動的確保せずにすませる、という工夫がされています。 64bit CPUだと、vector互換でもsizeof(ポインタ)*3=24bytesになりますし、結構ありがたい実装です。 (最近作ってた 俺俺string がまさにこのタイプだった... もうboostのでよく)

※ メモ: ビットフィールドは エンディアン問わず、先に書かれたものが低アドレスに配置される.

stable_vector, flat_map, flat_set

stl コンテナの代用品でなく、vector 実装をベースにした特化版。
stable_vector は、vector<T*> と要素を別途アロケートしたもの。(boost::ptr_vectorの類似品?)
flat_map,flat_set は、vector<T> (mapはT=pair<KEY,VALUE>) にソート状態で登録して、インターフェースがmapやsetになったもの。
各自で俺俺コンテナを作ってそうな類なので(ええ作ってました)、boost にあると楽になりそうです。 (できれば stable_vector を flat_map 化したものもあればうれしいところ)

詳しいことは他のサイトにあたってください。

scoped_allocator_adaptor

C++11 で増えた アロケータです。

scoped_allocator_adaptor を用いれば、例えば map<string,int> のようなコンテナで、ローカルなアロケータをstringとmapで共通で使えるようにできます。
※ 内側のコンテナ(この場合 string)のコンストラクタとして、デフォルトコンストラクタでなくアロケータを引数にとるコンストラクタが使われるようになります。

アロケータをコンテナのメンバーにするので、当然メモリー使用量も増えます(とくに内側のコンテナは数が多くなりやすく)。 ので、コンテナのメンバーにするアロケータはポインタだけにし、実装はさらにその先に用意、といった感じにすることになると思います。

詳しいことは こちら のサイトとかみてもらったほうがいいです。

正直なところ一見ではよくわからず、試してみてなんとなく納得の状態です。

以下、試してみたソース.

#include <stdio.h>
#include <boost/container/map.hpp>
#include <boost/container/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/foreach.hpp>

// mapで解放しないこと前提に、スタックで解放無視の簡易アロケータ.
template<unsigned N>
class SampleAlloc3 {
public:
    SampleAlloc3() : size_(0) { }
    void* allocate(unsigned n) {
        assert(size_+n <= N);
        void* p = ptr(size_);
        size_ += n;
        return p;
    }
    void  deallocate(void*, unsigned) { /*dummy*/ }
private:
    typedef double buf_t;
    void*       ptr(unsigned n=0) { return (char*)buf_ + ((n + sizeof(buf_t)-1) & ~(sizeof(buf_t)-1)); }
private:
    unsigned    size_;
    buf_t       buf_[N / sizeof(buf_t)];
};

// map<string,int> のメモリー
template<unsigned SAMPLE_ALLOC3_SIZE>
class Hoge {
public:
    Hoge() : strIntMap_(std::less<String>(), MapAllocator(StrIntPairAlloc(&smpAlc3_))) {}
    void insert(const char* name, int val) {
        //strIntMap_[String(name, &smpAlc3_)] = val;
        strIntMap_.emplace( name, val );
    }
    void printAll() {
        BOOST_FOREACH(StrIntPair p , strIntMap_)
            printf("%s = %d\n", p.first.c_str(), p.second);
    }
private:
    typedef SampleAlloc3<SAMPLE_ALLOC3_SIZE> Alloc;
    template<typename T>
    class SampleAllocator3 {
    public:
        typedef T value_type;
        // SampleAllocator3() : alc_(0) {}  // デフォルトコンストラクタは用意しないほうが安全.
        SampleAllocator3(Alloc* alc) : alc_(alc) { }
        template<typename U> SampleAllocator3(SampleAllocator3<U> const& r) : alc_(r.detail_get_ptr()) { }
        T* allocate(unsigned n) { return reinterpret_cast<T*>(alc_->allocate(n*sizeof(T))); }
        void  deallocate(T* p, unsigned n) { alc_->deallocate(p,n*sizeof(T)); }
        bool operator == (SampleAllocator3 const & ) const { return false; }
        bool operator != (SampleAllocator3 const & ) const { return true; }
        Alloc* detail_get_ptr() const { return alc_; }
    private:
        Alloc*      alc_;
    };
    typedef boost::container::basic_string<char, std::char_traits<char>, SampleAllocator3<char> >   String;
    typedef std::pair<const String, int>    StrIntPair;
    typedef SampleAllocator3<StrIntPair>    StrIntPairAlloc;
    typedef boost::container::scoped_allocator_adaptor< StrIntPairAlloc >           MapAllocator;
    typedef boost::container::map<String, int, std::less<String>, MapAllocator >    StrIntMap;
private:
    SampleAlloc3<SAMPLE_ALLOC3_SIZE>    smpAlc3_;
    StrIntMap                           strIntMap_;
};

void sample3()
{
    Hoge<1024>  hoge;
    hoge.insert("xyz", 1);
    hoge.insert("abcdefghijklmnopqrstuvwxyz", 2);
    hoge.insert("1234567890", 3);
    hoge.printAll();
}

emplace, emplace_back、単に(コピー)コンストラクタの頻度さげるだけじゃなくて、今回のような場合まさに必須の類でした。
共通で使うアロケータ(SampleAllocator3)には デフォルトコンストラクタをつけちゃダメ、というのを実感。
( デフォルトコンストラクタつけてると insertや push_back 使ってもコンパイル通るため... ハマりました)

おわりに

遅刻したうえに、 グダグダな内容ですみません。
(当初の目論見からずれまくり...そもそも deque 調べるため boost::container 見始めたはずが)

例に挙げたソースのとおり allocator の記述に関してはすごく楽でした。 が、boost::contaneier を std:: にかえて vc12 でコンパイルすると全く×だったので boost::contaneier に結構依存していると思います。

時間の余裕なくなってるので、たぶん後日 補足修正書くことになりそうですが...

おつきあいいただきありがとうございました。


15日目は、@yohhoy さんです。


追記: コンパイル試したときのソース



<<20112013>>