GIL

C++標準ライブラリの一部である STL をそれはもう好きで、世の中で最も美しくて革新的なライブラリだと思っているが、そんなSTLを生み出したステパノフは Adobeに移籍していたらしい。

んで最近、こんなライブラリが Adobeからリリースされてるではないですか。
Generic Image Library
http://opensource.adobe.com/gil/

すげー、こりゃあ今後に期待大だな。

sb の entry.php の復元

sbというblogシステムを使っているが、たまーにentry.phpという記事一覧を保持するデータファイルらしきものが壊れることがある。
古いバージョンのまま使ってるのが原因のような気がするが、今はアップデートする気力がないので、今回も自分でentry.phpを復元することにした。
もしかしたら同じ現象の人もいるかもしれないので、C++での復元プログラムをさらしてみる。sbのファイルフォーマットは自分で適当に想像したものなので、間違ってたらすみません。
ちなみにboostのfilesystemをはじめて使ってみたが、いいね。Unicodeに対応してない&将来にわたって対応の予定がないってところが痛いけど。
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/convenience.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/algorithm/string.hpp>
#include <map>

using namespace std;
using namespace boost;

int _tmain(int argc, _TCHAR* argv[])
{
    if (argc != 3) {
        cout << "usage: sb_entry_repair src_sb_entries_path dst_sb_entry_file_path\n";
        return -1;
    }

    namespace fs = boost::filesystem;
    fs::path entries_path = fs::path(argv[1], fs::native);

    const string sHeader("ent\t");  // entry header
    map<int, string> entry_map;
    fs::directory_iterator it_end;
    for (fs::directory_iterator it(entries_path); it != it_end; ++it) {
        if (".php" != to_lower_copy(fs::extension(it->leaf()))) {
            continue;
        }
        fs::ifstream ifs(*it, ios::binary);
        if (!ifs.is_open()) {
            continue;
        }

        string sLine;
        getline(ifs, sLine);
        if (sLine.compare(0, sHeader.size(), sHeader) != 0) {
            continue;
        }
        sLine.erase(0, sHeader.size());

        iterator_range<string::iterator> itrFind = find_nth(sLine, "<>", 9);
        if (!itrFind.empty()) {
            int iEntryNum = atoi(sLine.substr(0, sLine.find_first_of('<')).c_str());
            entry_map[iEntryNum] = copy_range<string>(make_iterator_range(sLine.begin(), itrFind.end()));
        }
    }

    ofstream ofs(argv[2], ios::binary);
    if (ofs.is_open() && !entry_map.empty()) {
        ofs << entry_map.rbegin()->first + 1 << endl;
        for (map<int, string>::const_iterator it = entry_map.begin(); it != entry_map.end(); ++it) {
            ofs << it->second << endl;
        }
    }

    return 0;
}

Foreach

Boost.Foreachは簡単に使えて、かつ、メリットも大きいライブラリだ。
forループを Perlなんかの foreachのように記述できるようになる。

たとえば配列だったら
double a[] = {1,2,3,4};

for (int i = 0; i < sizeof(a)/sizeof(*a); ++i) {
  a[i] *= -1;
}
なんて書いていたのを
double a[] = {1,2,3,4};

BOOST_FOREACH(double& d, a) {
  d *= -1;
}
てな感じにループカウンタなどを気にしないで書ける。 これがものすごーく快適。
ループの終了条件があっているかだとか、多重ループでは正しいカウンタをインクリメントしているのかだとかに労力を費やす必要がなくなるのは本当に大きい。

listなんかにも当然使えるのだけど、
for (std::list<int>::const_iterator it = l.begin(); it != l.end(); ++it) {
  std::cout << *it << "\n";
}
BOOST_FOREACH(int n, l) {
  std::cout << n << "\n";
}
と書けて、プログラマの意図するところも短く明確に伝わるようになる。

シーケンスとして扱えるものは配列やlistだけでなく、以下にあげたようにrangeになっているものならOK。さらに自分の作ったコンテナ型でもちゃんとコンセプトを満たせば利用できるようになる。
  • STLコンテナ(とその仲間たち)
  • 配列
  • NULL終端文字列 (char[], wchar_t[], char*, wchar_t*)
  • std::pair<Iterator, Iterator>


パフォーマンス面では、ドキュメントをみるかぎり
  • 動的メモリ確保や、仮想関数呼び出し、関数ポインタ呼び出しなどが一切なく、コンパイラの最適化を阻害しない。
  • 普通にループをハードコードした場合にくらべて数%程度のパフォーマンス犠牲しかない。
とのことなので問題になることはほとんどなさそう。

注意するべき点としては2つほどドキュメントで挙げられている。 1つはBOOST_FOREACHはマクロを使って実装されているので、カンマの数は1つでなくてはならない。
とすると、mapなんかの場合に問題になる。
std::map<char*, int> m;

BOOST_FOREACH(std::pair<char*, int> p, m) {  // コンパイルエラー! カンマが複数個ある。
  ..
}
これはtypedefによって回避するか
std::map<char*, int> m;
typedef std::pair<char*, int> P;

BOOST_FOREACH(P p, m) {  // OK!
  ..
}
あらかじめ内容を受け取る変数を宣言しておけばいい。
std::map<char*, int> m;
std::pair<char*, int> p;

BOOST_FOREACH(p, m) {  // OK!
  ..
}

もう1つはループ中に、ループの終了条件を表すイテレータ(end()で返すもの)を無効にするような操作を行ってはならないということ。
std::vector<int> v(4);

BOOST_FOREACH(int n, v) {
  v.push_back(1); // vectorだとpush_backによって 以前にend()によって返されたイテレータが無効になるのでアウト。
}
これはfor文を素直に使うしかない。


ところでBOOST_FOREACHはマクロを使って実装されているのだが、マクロの有名な副作用、つまり引数が複数回評価されて大変な事態を引き起こすことがないように変態テクニックを駆使して実装されている。
以下のようなコードでも GetStringFromStream()関数が呼び出されるのは1回限りであることが保証されている。
BOOST_FOREACH(char c, GetStringFromStream()) {
  ...
}

どのようなトリックを使って実現しているかは このページに記述されているが、とっても面白いので興味のある人は一読をオススメする。
記事のタイトルが "Conditional Love"(訳: 条件演算子、愛してるよ、条件演算子)になっているのが最初意味不明だったが、読み進めていくうちに謎が解き明かされていく。
C/C++の世界では悪役にされがちな条件演算子(?演算子)が、BOOST_FOREACHではとてつもなく重要な働きをしていることに感動すら覚えマスタ。

<< 4/4