hoge diary - November 2007

November 27, 2007

[雑記] mogrify の覚え書き

画像に文字を重ね書きするために ImageMagick の convert や mogrify (大抵 mogrify) を使っていますが,何度使っても忘れるので覚え書き.

mogrify -annotate +x+y "(描画する文字列)" -font "(フォント)" -pointsize (ポイントサイズ) -fill (文字の塗りつぶし色) -stroke (文字の輪郭色) -strokewidth (線幅) (ファイル名)

(例: Hoge.bmp に hogehoge という文字を緑色で描画する)
mogrify -annotate +100+100 "hogehoge" -font "Arial" -pointsize 100 -fill "#00FF00" -stroke "#008800" -strokewidth 3 Hoge.bmp

November 10, 2007

[映画] バイオハザード3

バイオハザード3 (英題: Resident Evil: Extinction) 観に映画館行ってきました.

面白かったー.

アリスかっこ良かった.戦闘シーン観てるとまるで自分がゲームやってるみたいな気分.

上から見下ろす視点とか,徐々に引いてゆくカメラワークがこのシリーズで頻繁に使われまくってますが,これがかなりお気に入り.

また観たいですなー.

November 9, 2007

[映画] Today's Movie

今日は「ボーン・スプレマシー」(英題: The Bourne Surpremacy) です.これも例によって明日公開の「ボーン・アルティメイタム」に関連しての放送で,映画館でスプレマシーを見てなかったので観ることに.ちなみに,supremacy とは英語で「優位」「支配」とのこと.

例によってあらすじは,調べると出ますのでここには書きません.

ストーリーが進んでいくパターンは前作と同じ感じで,ボーンが追われるのがメイン.でも今作は前作より追われ方が激しい.

で,観終えた感想はと言いますと,なかなか面白い映画でした.ただ,派手なアクション映画が好みの自分にとってはボーンの活躍が地味に見えて,ちょーっと物足りない感じ.

以下余談.

序盤の舞台はベルリンなので,ボーンを追う警官が走りながら「POLIZEI!!, POLIZEI!!」と連呼してます.それ以外の台詞はなし.

えーと... 観客に対する分かりやすさへの配慮なのか,はたまたドイツの警察がそういうものなのか... は不明ですが,せめて「待てー」とか「止まれー」とかドイツ語で喋って欲しかった.

[C++] VC2005 とテンプレートと friend

テンプレートクラスの中で関数に対する friend 宣言とその関数の定義を書くと... VC2006 のコンパイラはそれぞれを別物として扱い,結果,呼び出し時に「あいまいである」と C2593 エラーを出してくれます.

今回コンパイルしようとしたのは下のコード.テンプレートクラス Hoge の中で,テンプレート関数を friend 宣言して,クラスの外にその関数の定義を書いたものです.

#include <iostream>

namespace N {

template <size_t A, size_t B>
class Hoge
{
public:
  template <size_t A_, size_t B_>
  friend inline Hoge<A_,B_> operator * ( const Hoge<A_,B_>& a, const Hoge<A_,B_>& h );
};

template <size_t A, size_t B>
inline Hoge<A,B> operator * ( const Hoge<A,B>& a, const Hoge<A,B>& h )
{
  return Hoge<A,B>();
}

}

int main()
{
  N::Hoge<3,4> a;
  N::Hoge<3,4> b;
  N::Hoge<3,4> c = a*b;
  return 0;
}

これを VC2005 Express SP1 のコンパイラ (cl.exe 14.00.50727.762) でコンパイルすると... C2593 エラーが発生します.

$ cl /EHsc /Ox /MD /Oy /GF /Zc:wchar_t /Zc:forScope /nologo /c /Fo"test.obj" "test.cc"
test.cc
test.cc(44) : error C2593: 'operator *' があいまいです。
        test.cc(12): 'N::Hoge<A,B> N::operator *<3,4>(const N::Hoge<A,B> &,const N::Hoge<A,B> &)' [引数依存の照合を使用して検出しました] 、
        with
        [
            A=3,
            B=4
        ]
        test.cc(22): または 'N::Hoge<A,B> N::operator *<3,4>(const N::Hoge<A,B> &,const N::Hoge<A,B> &)' [引数依存の照合を使用して検出し ました] の可能性があります
        with
        [
            A=3,
            B=4
        ]
        引数リスト '(N::Hoge<A,B>, N::Hoge<A,B>)' を一致させようとしているとき
        with
        [
            A=3,
            B=4
        ]
$

ここでコンパイラが曖昧だと言っている 2 つの関数,よく見るとシグネチャが同じです.

  • test.cc(12): 'N::Hoge<3,4> N::operator *<3,4>(const N::Hoge<3,4> &,const N::Hoge<3,4> &)'
  • test.cc(22): 'N::Hoge<3,4> N::operator *<3,4>(const N::Hoge<3,4> &,const N::Hoge<3,4> &)'

VC2005 SP1 のコンパイラはこの 2 つを別物だと扱ってしまっているようです.g++ 3.4.4 では問題なくコンパイルできました.

調べてみると,同じ内容のフィードバックを発見.が,しかし,このフィードバックの状態と書かれた項目を見ると,終了 (修正しない)となっており,VC2005 (SP1 含む) では直さないようです.

これは困った.VC2008 では直ってるのかな.

November 8, 2007

[プログラミング] Rake を使ってみる

Rake という新しいオモチャに手を出している今日この頃.Rake というのは make っぽい機能を提供してくれる Ruby のモジュールです.

make と比べると,make が文字列しか扱えないのに対して,Rake は Ruby の上で動くので,もう文字列,配列,ハッシュなどを慣れ親しんだ Ruby の機能でいろいろ操作できます.便利.

もう 1 個の利点といえば... cp や rm といったコマンドの名前が環境ごとに異なるということを考えなくともよいということでしょうか.書き手は FileUtils の関数を使って Rakefile (make でいう Makefile です) を書いておけば,あとは Ruby が吸収してくれるので楽です (コンパイラやリンカはコマンドが存在しないとダメですが...).

さて,Rake の使い方はいでさく氏のサイトに詳しく書いてあるのでそちらを参照するとして (私もそこで学びました),一通りの使い方が分かったところでさぁ書いてみようと,まずは以下の Rakefile を作りました.

TARGET = "test"

# Rake は最初に default と名づけられたタスクを実行する
task :default => TARGET

SRCS = FileList[ "test.cc", "test2.c" ]
OBJS = SRCS.ext('o')

# OBJS をリンクして実行ファイルを作る
file TARGET => OBJS do |t|
  sh "g++ -o #{t.name} #{t.prerequisites.join(' ')}"
end

# .o のファイルは .cc や .c から作られるということを教える.
# ファイル名を明示せずとも,Rake が自動的にこのルールを適宜当てはめてくれる.
rule ".o" => ".cc" do |t|
  sh "g++ -c -o #{t.name} #{t.source}"
end

rule ".o" => ".c" do |t|
  sh "gcc -c -o #{t.name} #{t.source}"
end

これを test.cctest2.c のあるディレクトリで実行すると,次のようにちゃんとビルドが実行できます.

$ rake
(in /home/hoge)
g++ -c -o test.o test.cc
gcc -c -o test2.o test2.c
g++ -o test.exe test.o test2.o
$

うまく動きました.test.cc だけ touch してから再実行すると,make と同様にちゃんと test.cc だけ再コンパイルしてくれます.

ここで,ソースコードは全て ./src に,オブジェクトファイルは全て ./objs に分けたい場合は,rule ".o" => ".cc" が使えない(マッチしない)ので,Rakefile を次のように書く必要があるみたいです.

TARGET = "test"

task :default => TARGET

OBJ_DIR = "objs"

SRCS = FileList[ "src/test.cc", "src/test2.c" ]

directory OBJ_DIR

file TARGET => OBJS do |t|
  sh "g++ -o #{t.name} #{t.prerequisites.join(' ')}"
end

# SRCS の各要素に対して 1 個ずつファイルタスクを作る
SRCS.each do |src|
  obj = src.sub( %r{^src/(.*)\.(cc|c)}, OBJ_DIR+'/\1.o' )
  file obj => [OBJ_DIR, src] do
    sh "g++ -c -o #{obj} #{src}"
  end
end

コンパイル前にオブジェクトファイル用のディレクトリを作ってもらわないといけないので,directory タスクを付け加えています.

最初は下のように書いてみたりもしましたが,Rake を実行時に objs/test.o の作り方が分からんと言われてしまいました.

TARGET = "test"

task :default => TARGET

# ソースは src ディレクトリ
SRCS = FileList[ "src/test.cc", "src/test2.c" ]

# 中間ファイルは objs ディレクトリ
OBJS = SRCS.sub(/^src\//, 'objs/').ext('o')

file TARGET => OBJS do |t|
  sh "g++ -o #{t.name} #{t.prerequisites.join(' ')}"
end

rule ".o" => ".cc" do |t|
  sh "g++ -c -o #{t.name} #{t.source}"
end

rule ".o" => ".c" do |t|
  sh "g++ -c -o #{t.name} #{t.source}"
end

それにしても,Rake 面白いですね.

November 7, 2007

[雑記] 開発途中バージョンの TODO

プログラムを書いているとき「あー,こんな細かい処理を考えるのなんて後回しにしよう!」と思った瞬間,その部分のコードを暫定的なもので済ませておいて,コメントに「TODO: (後回しにした作業)」と書いています.こうすれば,暇になったときにファイル全体を "TODO" で検索し,後回しにしていた作業を行える,というわけです.これは大半の人が行っていると思います.

が,開発途中だと,動くものを作るのが先なので,書いた TODO: はかなり後までそのままの状態で残りますなぁ...

November 5, 2007

[プログラミング] 3x3逆行列の計算は直接やった方が速い

まあ,わざわざ書くまでもないことかも知れないのですが.

3x3くらいの逆行列だったらライブラリ使うより自分で計算した方が速かった,という話です.

対戦相手は CLAPACK + CPPLapack の組み合わせ.

使った CLAPACK は Version 3.0 で,ATLAS は使っていません.CPPLapack は 2007/10/28 に svn checkout したものです (2005_03_25 版だと下のコンパイルエラーが出るので,当時の最新版をチェックアウトして使いました.そしてごめんなさい.チェックアウトしたリビジョンを忘れてしまいました).

dgbmatrix-misc.hpp(176) : error C2061: 構文エラー : 識別子 'A'
dgbmatrix-misc.hpp(178) : error C2440: '=' : 'double **(__cdecl *)(void)' から 'double **' に変換できません。この変換が可能なコンテキストはありません。

話が脱線しますが,2005_03_25版で,上のエラーが出ている行を見ると,下のように書いてあります.

double** A_darray(A.Darray);

これは double** の値を初期化するつもりで書いていますが,コンパイラは関数宣言に見えるものは全て関数宣言として扱うので,これを関数宣言として扱おうとしてコンパイルエラー,というわけです.

最新版では次のように書いてありますので問題ありません.

double** A_darray = A.Darray;

上の CPPLapack のエラーに関する話は,Belution.com VC++ フォーラムのどこかに書いてあったものです.ですが,残念なことに今 Belution.com のサーバがダウンしているようで全くフォーラムを見られません.

閑話休題.まずは CLAPACK+CPPLapack を用いた場合の逆行列計算関数(Inverse1)のソースを載せます.

CPPL::dgematrix Inverse1( const CPPL::dgematrix& mat )
{
  return CPPL::i(mat);
}

まあ,これは CPPLapack の関数を呼んでいるだけです (しかし... 逆行列の関数名が i とは...).

続いて,手計算バージョン(Inverse2)のソース.

CPPL::dgematrix Inverse2( const CPPL::dgematrix& mat )
{
  CPPL::dgematrix ret(3, 3);

  double a = mat(0, 0);
  double b = mat(0, 1);
  double c = mat(0, 2);
  double d = mat(1, 0);
  double e = mat(1, 1);
  double f = mat(1, 2);
  double g = mat(2, 0);
  double h = mat(2, 1);
  double i = mat(2, 2);

  double det = (a*e*i + b*f*g + c*d*h) - (a*f*h + b*d*i + c*e*g);

  ret(0, 0) = (e*i-f*h)/det;
  ret(0, 1) = (h*c-i*b)/det;
  ret(0, 2) = (b*f-c*e)/det;
  ret(1, 0) = (f*g-d*i)/det;
  ret(1, 1) = (i*a-g*c)/det;
  ret(1, 2) = (c*d-a*f)/det;
  ret(2, 0) = (d*h-e*g)/det;
  ret(2, 1) = (g*b-h*a)/det;
  ret(2, 2) = (a*e-b*d)/det;

  return ret;
}

それぞれを 1000000 回ずつ呼び出して,実行時間を計測しました.WinXP(SP2)+VC8(Express)です.また,時間計測には高分解能パフォーマンスカウンタ(QueryPerformanceCounter() API)を使いました.

その時間計測結果がこちら.

関数名時間[秒]
Inverse11.403501
Inverse20.400147

ええ,3x3くらいなら自前で計算した方が速い,というわけです.

そして最後にオチ.「逆行列を100万回計算してもたった 1.4 秒しか掛かってないので,こんなところを速くしても全体は速くならないっ!」(アムダールの法則)

以上.おあとがよろしいようで.

November 4, 2007

[C++] メンバコンストラクタの例外

普段からこれっぽっちも疑問に思わなかったんですが,クラスメンバのコンストラクタの例外をキャッチする方法がある(cppll:3219)とのこと.

構文はこんな感じ.

class Foo
{
  Bar bar;  // Bar クラスは Foo のメンバ (定義は省略)
  Hoge() try : bar() {
    // メンバのコンストラクトが成功したときは,
    // ここに書いた Hoge のコンストラクト処理が行われる
    ...
  }
  catch (...) {
    // メンバのコンストラクタが例外を投げたときはここに来る
    ...
  }     // ←ここで自動的に catch した例外が再度投げられる (VC7 は投げてくれないらしい)
};

こんなところに try-catch が書けたんだ... 全然知らなかった...

メンバコンストラクタが投げた例外は catch 節を抜けた後で自動的に re-throw されるようになっているため,そのクラス自身のコンストラクトも失敗します (cppll:3221 によれば,VC7 では標準の動作をさせるために明示的に再スローしないとダメらしいです.手元に VC7 がないので確認できないのですが).

g++-4 や VC8 (Express Edition) ではどうなるかを試してみることに.

#include <iostream>
#include <stdexcept>

using namespace std;

class B
{
public:
  B() {
    throw runtime_error("hoge");
  }
  ~B() {
    cout << "~B()" << endl;
  }
};

class A
{
public:
  // constructor
  A() try : b() {
    cout << "A()" << endl;
  }
  catch (const exception& e) {
    cout << "foo" << endl;
  }

  ~A() {
    cout << "~A()" << endl;
  }

private:
  B b;
};

int main()
{
  try
  {
    A a;
  }
  catch (const exception& e)
  {
    cout << "bar" << endl;
  }
  return 0;
}

g++-4.1.2 (Gentoo 4.1.2) と,VC8 Express Edition (14.00.50727.42) で試したところ,どちらもいずれの実行結果が得られました.

(g++-4.1.2 での結果)
$ g++ test.cc
$ ./a.out
foo
bar
$
(VC8 Express での結果)
$ cl /nologo /EHsc test.cc
test.cc
$ ./test
foo
bar
$

ちゃんと A のコンストラクトは失敗しており,いずれも標準の動作になっています.

November 3, 2007

[映画] Today's Movie

バイオハザードII アポカリプス (英題: Resident Evil: Apocalypse) です.

本日公開の3に先立って,自分みたいに映画館に行くのを忘れた人のため(?),フジテレビ系で2をやってくれるので観ることに.

前作よりゲームっぽくなってていいです.あと2の新キャラ,ネメシス(Nemesis)がいい味出してます.最初から最後まで...

さて... 忘れないうちに3観に行かないと.


Valid XHTML 1.1! Valid CSS!
© 2004-2009 ぱくちゃん.
Last modified: Tue Nov 27 15:25:07 JST 2007