コンテナと for ループ
次のようなコード断片、 iterator を明示的に使う for ループが見苦しく感じる。
void hexstr( const std::vector<unsigned char>& bytes, std::vector<char>& out) { for(std::vector<unsigned char>::const_iterator iter = bytes.begin(); iter != bytes.end(); ++iter) { int byte = (int)*iter; const char hexchars[] = "0123456789ABCDEF"; out.push_back(hexchars[(byte>>4)&15]); out.push_back(hexchars[byte&15]); } }
STL アルゴリズムを使えば、 iterator を陽に使わずに済ませられる。
#include <iterator> #include <numeric> #include <vector> typedef std::vector<unsigned char> ucs_t; typedef std::vector<char> cs_t; namespace { template<typename T> inline T aux(T it, unsigned char v) { static const char alphabet[] = "0123456789ABCDEF"; *it++ = alphabet[v / 16]; *it++ = alphabet[v % 16]; return it; } } void hexstr(const ucs_t& ucs, cs_t &cs) { std::accumulate(ucs.begin(), ucs.end() , std::back_inserter(cs), aux<std::back_insert_iterator<cs_t> >); }
残念なところは、 for ループならそばに定義できる処理を、別の関数として実体定義しなければならないこと。
C++0x の lambda で積算
C++0x の lambda を使えば回避できる…気がするけれど、手元の g++ 4.2 はまだ lambda をサポートしていないため試せない。入れてみるか…?
C++0x の lambda を使うと、この不便は回避できる。
void hexstr(const ucs_t& ucs, cs_t &cs) { std::accumulate(ucs.begin(), ucs.end(), std::back_inserter(cs) , [](std::back_insert_iterator<cs_t> it, unsigned char v) { static const char alphabet[] = "0123456789ABCDEF"; *it++ = alphabet[v / 16]; *it++ = alphabet[v % 16]; return it; } );
g++4.5 で lambda を有効にするには -std=c++0x をオプション引数で渡すこと。
もう一点、この例の lambda 式のひとつめの引数の型について。 STL を使っていて悩むことのひとつに、値をつくりだすユーティリティを使って構築した値の型の特定が難しいことがある。 std::back_inserter(cs) の返す値の型がなんになるか、ライブラリーのドキュメントを調べるにせよ、あるいはコンパイルしてみてエラーから特定するにせよ、いずれにせよ面倒である。これについて C++0x で導入される decltype を使ってコンパイラーに型の判断をまかせることが可能である。
ここでは std::back_insert_iterator
外部環境をキャプチャーして積算
先の accumulate は、結果を収集するコレクティングパラメーターを明示するために accumulate アルゴリズムを使用したけれど、外部環境を手軽にキャプチャーできる lambda の特性を活かしてみてもよい。
次のようにすれば、もともとの for ループでの実装に近くなる。
void hexstr(const ucs_t& ucs, cs_t &cs) { std::for_each(ucs.begin(), ucs.end(), [&cs](unsigned char v) { static const char alphabet[] = "0123456789ABCDEF"; cs.push_back(alphabet[v / 16]); cs.push_back(alphabet[v % 16]); }); }