某日記

(前期)

平成19年12月1日(土曜日)

今日

ドトールでマターリ。

バスを待っていたら、目の前の看板に「ありがとうございます」と書いてあった。この言葉はもともと「ありがたく」+「ございます」にウ音便を適用した言葉だろう。それならばアクセントは「と」にあってしかるべきな気がするのだが、少なくとも関東では「り」にある。どうしてこういうことになっているんだろう。

GPLv2 の派生物において、あまり守られていない条項の一つは 2 の (a) だと思う。まじめに守ったら Linux カーネルは日付まみれになる。

C++: デストラクタは例外を投げてはいけない

さて、何ででしょう。

C++ の仕様は、コンストラクタやデストラクタが例外を投げてはいけないとは書いていないし、「注意事項」さえ守れば投げてもいい。実際、コンストラクタが例外を投げるのはそれほど悪いことではない。

一方でデストラクタが例外を投げるようなコードは大抵良くない。何故かというと、上記「注意事項」の一つであるところの「例外によるスタック巻き戻し中に別の例外を投げてはいけない(いわゆるダブルフォルトの禁止)」を抵触しないようにプログラムを書くのが難しくなるから。

たとえば次のようなプログラムを見てみよう:


一見何の問題も無さそうなこのプログラムが、実際には正しく動かない。これを見抜くのには熟練が必要だ(それに気づくことさえできれば、これをとりあえず正しく動くようにするのはさほど難しくないのだが)。熟練を必要としない唯一の方法は「デストラクタでは絶対に例外を投げない」というルールを守るしかない。 

まあでも、どうしても投げたくなることってのもあるので難しい。上の例はその典型で、つまりある種のアサーションをデストラクタでやりたくなる場合がある。どちらかといえば、エラーの時にはデストラクタのアサーションを無効にするような仕掛けが必要という話かもしれない。いずれにしても何らかの明示的な仕掛けが必要になるので、これはアサーションとしては全く良くない。

結局、真の問題はアサーションを行うのがデストラクタしかないというところにある。これは代入という原因によって引き起こされる結果を、明示的な形で取り出していないということに起因している。結果を取り出す部分があれば、そこでチェックすれば済む。もう少し考えてみれば、上のクラス A は何らかの副作用を以て代入に報いているのだろうと想像できる。結局、副作用に頼るプログラムは良くないということだろう。関数が明示的な引数と戻り値を持つべきであるのと同様に、クラスは明示的な入力と出力を持つべきである。

しかし、副作用を排したとしても、結果を取り出すのを忘れるような粗忽に対するアサーションはどこに書けばいいのか、という問題は残る。たぶんそんなことを気にする俺がパラノイアなんだろう。

一方で、コンストラクタが例外を投げても*良い*理由を言っておくと、「一つだけコンストラクタ特有の注意事項があるのを除けば、せいぜい普通の例外安全性だけ気にすれば良いから」となる。このへんは割と考えてあって、規格に従って実装すれば完璧に処理できる。

ここで「コンストラクタ特有の注意事項」というのは「コンストラクタで例外が発生すると、そのクラス自体のデストラクタが呼ばれなくなる」ということで、これは実際には大したことではないし、いずれにしてもこの「特別な注意事項」への配慮はクラス実装の中で完全に閉じることができる。もちろん普通の例外安全への配慮はクラスの外でも必要。

これと比較して、デストラクタで例外を投げた場合、少なくともクラス実装だけでダブルフォルトを防ぐことは不可能だし、そもそもデストラクタで例外を投げさえしなければ、まず絶対にダブルフォルトは起こらない。もちろん、デストラクタが例外を投げたとしてもダブルフォルトが起こらないようにプログラムすることは不可能ではないのだが、デストラクタが例外を投げてしまうばっかりに、覚えなければならない概念(=ダブルフォルト)が増えてしまう。これは、投げることによる利益と比較すると大きな代償だ。だから、デストラクタは例外を投げちゃいけない。

平成19年12月2日(日曜日)

今日

家で寝てた。

東海林さだお先生言うところのセコズルを目の当りにしたので笑った。それで勝たれるとムカツクが、負けたんだから許してやろう。

小人閑居して云々というけれど、閑居してなくてもズルできるのは一つの才能だと思う。

ところで「閑居(閒居)」という言葉には二通りの解釈があって、日本では「暇人はろくなことをしない」という方便みたいに使われているけれど、原典であるところの大学の当該箇所を読んでみると、これを暇人と解釈してしまうとこの一文だけ全体から浮いてしまう。だから、「人影の疎らなところにいると」という解釈のほうがマッチしているように思える。

しかし、居は居でも閑居云々より中居リーダーの生え際の方が深刻だと思った。

平成19年12月3日(月曜日)

今日

crypton 製品で最近気になってるのは、VOCALOID じゃなくて Real Strat なんだよな。

評価版使ってみたが、カッティングがいまいちな他は割とよさげ。特に、スライドでフラットを跨ぐときのざらつき感が、微妙に嘘くさいけど表現されているのが良い。普通のサンプラーでもできなくはないがサンプルマッピングを作るのが面倒。

そもそも、エレキギターはいいマルチサンプルが少なくてねぇ。手元には一本だけ気に入ってる奴があるけど、一本だけじゃあいろいろ辛い。特にギブソン系が無い。レスポールと 335 の決定版みたいなのがあればいいんだが。

Vista の DOS 窓では DOS/V アプリケーションで日本語が出ないらしい。MARS for MS-DOS が使えないぢゃないか。

最近あまり使われなくなった言葉に「いい年して」というのがあるけれど、これは老若男女問わず自重を促すのに有効なタガの一つだった(子供には最初から自重を期待してない)。しかるに最近は、年長者が率先して年甲斐のないことをやるもんだから、このタガが外れてしまっている。それはそれで悪くはないとしても、これに代わる良いタガはないもんか。人間なにかにつけてタガは必要だ。

「お若いですね」と言われて喜ぶような老人にはなりたかねえやと思うけれど、実際に年寄りになった時にどうなっているか自信はない。

平成19年12月4日(火曜日)

今日

飛び込みとか、他人に迷惑をかけるような自殺への罰則を重くするのはどうだろう。たとえば刑法に「飛び込み自殺をしたものは此の世で受けた 10 倍の苦しみを地獄で受ける」と明記するというのはダメですかそうですか。

こういうジョークが法律の中にあってもいいような気もする。

「プログラマ 35 歳定年説」を誰が言い出したかは知らないが、元ネタは菊池寛の「編集者 35 歳定年説」だと思う。たぶんロクでもない本を読んで感化された誰かがコンピュータ業界に持ち込んだんだろう。

平成19年12月5日(水曜日)

今日

Google、「有料リンク(Paid Links)がなぜいけないか」を明確に説明 - 僕は「Google にとって都合が悪いから」という説明のほうが好きです。これなら一文で済むし。何が行われているのかさえ明確ならばそれで十分。ぐぐるさんは時としてそこが怪しい。

平成19年12月6日(木曜日)

最近の査収物

P2!(5) / 江尻 立真

キスよりも早く(2) / 田中 メカ

君とひみつの花園(2) / 林 みかせ

赤髪の白雪姫 / あきづき 空太 - これはいいものだ

ひとひらの恋が降る(2) / やぶうち 優

シシ12か月 / わかつき めぐみ

ココにいるよ!(1) / 遠山 えま

ヤンキーフィギュア(1) / ミッチェル田中

異国迷路のクロワーゼ(1) / 武田 日向

今日

コミック高岡に DTM マガジンが置いてあるのは何かが間違ってる。

トイレットペーパーのスケジューリング問題。ペーパーホルダーが二つ並んでいるトイレがあるけど、あれは取り替えの頻度が半分で済むメリットはあってもバックアップとしてはほとんど用を為さない。人間は本能で両方を満遍なく使おうとするから、片方が切れるときはもう片方もほどなく切れる。必ずしも平等なスケジューリングが良いとは限らない例。私は常に少ない方を選択することにしている。

上の話は、両方にあまり差がないことを前提にしているけれど、あきらかに両方の分量に差がある時に、人間がどう振る舞うかは興味深いところだと思う。まあそれは調べてみないと何とも言えないから置いておいても、おそらくこの装置をバックアップ目的に使うためには、如何にして両方の残量に差を付けるかがポイントだと思う。

一つのアルゴリズムとしては、定期的にポーリングして、ある程度残量が減ったところで、残りの多い方を新品に取り換えてしまう方法かもしれない。もともと付いていた奴は在庫に戻す。ただ、この方法には安定性に難があって、中途半端な在庫をうまく解消する方法が見付からないと倉庫が溢れる。なかなか難しい。

結局、筋の悪い装置はいくらアルゴリズムでなんとかしようとしても難しい。ペーパーホルダーを二つ並べるより、ペーパー供給装置の付いたペーパーホルダーの方が、バックアップ目的には筋が良い。

イヤホンで気付かず? 大学院生が電車に接触死 - 「携帯電話で通話中、話の流れでおじぎした瞬間に電車が入ってきて以下略」っていう都市伝説を思い出した。

平成19年12月9日(日曜日)

週末

大阪行ってきた。行き銀河、帰りワイドビューしなの+長野新幹線。

銀河は金曜だけはいつ乗っても混んでるんだよなぁ。今回も満席。

サービスは四つに分けられる。つまり、「儲かるし客も喜ぶ」「儲からないけど客は喜ぶ」「儲かるけど客は喜ばない」「儲かりもしないし客も喜ばない」だ。「儲からないけど客は喜ぶ」サービスをうまいこと「儲かるし客も喜ぶ」ものに転換できる企業を良い企業と言う。ブランドイメージというのはこういうところから生まれてくる。

金曜の銀河には現状でも明確な需要があるのになぁ。需要を生かせないのは怠慢と言われても仕方がない。JR はとにかく世の中の流れのせいにしようとしているけど、もうちょっと夜行がなんとかなっていた 10 年前の時点でテコ入れしなかったのは営業努力が足りないと批判されてしかるべきだと思うよん。

先人はすごいハックをしている。わかりやすい例を挙げればフロッピードライブのシーク音で音楽を鳴らす類のことだ。こういう大胆なものは珍しいにしても、ゲームプログラムにはそういうハックが溢れている。20 年くらい前に行われたそうしたハックを 21 世紀の今になって再発掘している一握りの人々がいるはずなのだが、せっかく再発掘されても NDA やら著作権やらで呪われているので再び地下に埋もれてしまう。これはもったいない。なんとかして記録に残せないものか。

body and soul といえば Billie Holiday だが、ようつべで探して見付かったのは Billy Taylor だった。RIAA あたりから呪われるのでリンクはしない。

ビリーテイラーは日本じゃ全然知られてないビアニストなのだが(ウィキペ日本語版にエントリがない程度にはマイナー)、その道の人々には「博士号を持つ黒人ジャズプレイヤー」としてよく知られている(1921 年代生まれの黒人ということに注意)。その知識を生かしてクラシックの要素をいち早くジャズに持ち込んだ一人で、実際、ハードバップ時代を生き抜いたピアニストの割に妙に垢抜けたピアノを弾くから、ジャズに慣れていない人でも聴きやすいと思う。まあ悪く言えばアクがなくて面白みにかけるところがあるわけだけど、私は昔から割と好きだ。

インテリさんなので微妙に鼻持ちならないところがあったりして、上の動画でもイントロを左手一本で弾いてみたり、これがまた上手いもんだから微妙にいけ好かない。それも愛嬌の域ではあるが。

平成19年12月10日(月曜日)

今日

ねもい。

GDI+: アルファ合成について

GDI+ の合成方法には Over と Copy の二つの指定がある。一見簡単そうだが実際に行われることの詳細をドキュメントから知るのは難しい。

いろんなケースについて実験するプログラムを作って、結果を出力すると以下のようになる:

(1) COPY: (dst:ARGB8888)0x80000000+(src:ARGB8888)0x80808080=0x807F7F7F
(2) COPY: (dst:ARGB8888)0xFF400040+(src:ARGB8888)0x80808080=0x807F7F7F
(3) OVER: (dst:ARGB8888)0x00000000+(src:ARGB8888)0x80FF00FF=0x80FF00FF
(4) OVER: (dst:ARGB8888)0x80000000+(src:ARGB8888)0x80FF00FF=0xC0AA00AA
(5) OVER: (dst:ARGB8888)0x80FF00FF+(src:ARGB8888)0x80000000=0xC0550055
(6) COPY: (dst:RGB888)0xFF000000+(src:ARGB8888)0x80808080=0xFF404040
たとえば一行目は、ARGB 値が 0x80000000 なピクセルの上に、Copy モードで 0x80808080 なピクセルを DrawImage で重ねた結果得られた ARGB 値が 0x807F7F7F だったことを示している。

最初の 2 行を見れば、計算誤差が乗っかっていることを除けば、src のピクセル値がそのまま dst へとコピーされていることがわかる。まあこれはいいだろう。

OVER の 3 つはちょっとわかりにくい。まず(3)、dst がもともと 0 ならば、src のピクセル値がそのままコピーされている。これもいいとして、その次の 2 つが難しい。

Porter/Duff オペレータというものを知っていれば、これが Over オペレーションそのものだというのがわかる。src の正規化色ベクトルを (As, Rs, Gs, Bs)、dst のを (Ad, Rd, Gd, Bd) とすれば、結果 (Adr, Rdr, Gdr, Bdr) は次のような式で示される:

| Adr |     | As |            | Ad |
| Rdr |     | Rs |            | Rd |
| Gdr |  =  | Gs |  +  (1-As) | Gd |
| Bdr |     | Bs |            | Bd |
ただしここで全ての色ベクトルは premultiplied alpha で表現されているものとする。上の実験におけるピクセル値は non-premultiplied alpha なので次のような式になる:
Adr = As + (1-As) * Ad
Rdr = (Rs*As + (1-As) * Rd*Ad) / Adr
Gdr = (Gs*As + (1-As) * Gd*Ad) / Adr
Bdr = (Bs*As + (1-As) * Bd*Ad) / Adr

(4) で計算してみよう。dst のピクセル値 0x80000000 に対応する色ベクトルは (0x80/0xFF, 0/0xFF, 0/0xFF, 0/0xFF) = (0.5020, 0, 0, 0) 、src のピクセル値 0x80FF00FF に対応する色ベクトルは (0.5020, 1, 0, 1) となる。この non-premultiplied alpha な色ベクトルに Over の式を適用すると、

Adr = 0.5020 + (1-0.5020)*0.5020 = 0.7520
Rdr = (1*0.5020 + (1-0.5020) * 0 * 0.5020) / 0.7520 = 0.6676
Gdr = (0*0.5020 + (1-0.5020) * 0 * 0.5020) / 0.7520 = 0
Bdr = (1*0.5020 + (1-0.5020) * 0 * 0.5020) / 0.7520 = 0.6676
結果のピクセル値は (0.7520*255, 0.6676*255, 0*255, 0.6676*255) = (192, 170, 0, 170) = 0xC0AA00AA で一致している。

(5) も同様に:

Adr = 0.5020 + (1-0.5020)*0.5020 = 0.7520
Rdr = (0*0.5020 + (1-0.5020) * 1 * 0.5020) / 0.7520 = 0.3324
Gdr = (0*0.5020 + (1-0.5020) * 0 * 0.5020) / 0.7520 = 0
Bdr = (0*0.5020 + (1-0.5020) * 1 * 0.5020) / 0.7520 = 0.3324
で、(192, 85, 0, 85) = 0xC0550055 とやはり一致する。

(6) は dst が RGB バッファなので、上の式で Ad と Adr が 1 で固定される:

Adr = 1
Rdr = Rs*As + (1-As) * Rd = 0.5020*0.5020 + (1-0.5020) * 0 = 0.2520
Gdr = Gs*As + (1-As) * Gd = 0.5020*0.5020 + (1-0.5020) * 0 = 0.2520
Bdr = Bs*As + (1-As) * Bd = 0.5020*0.5020 + (1-0.5020) * 0 = 0.2520
これが 0xFF404040 になるのは言うまでもない。

おっと、(6) は Copy だった。「どうして Copy なのに値が変わってるの?」という疑問なわけだ。これは premultiplied ←→ non-premultiplied 変換が関係している。Copy は premultiplied な場合には自明で次のようになる:

| Adr |     | As |
| Rdr |     | Rs |
| Gdr |  =  | Gs |
| Bdr |     | Bs |
これを non-premultiplied なケースに変換するとこうなる:
Adr = As
Rdr = Rs*As/Adr
Gdr = Gs*As/Adr
Bdr = Bs*As/Adr
Adr に自由な値を代入できるのならば As と Adr は常に等しいから、As/Adr は 1 になる。結果として全てのコンポーネントがソースと同じ値になるけれど、(6) は dst が RGB888 なので、A コンポーネントは強制的に 1 にされる:
Adr = 1
Rdr = Rs*As/Adr = Rs*As
Gdr = Gs*As/Adr = Gs*As
Bdr = Bs*As/Adr = Bs*As
これはアルファチャネルの目的を考えればリーズナブルだ。

計算をやりなおしてみよう:

Adr = 1
Rdr = Rs*As = 0.5020*0.5020 = 0.2520
Gdr = Gs*As = 0.5020*0.5020 = 0.2520
Bdr = Bs*As = 0.5020*0.5020 = 0.2520
これが 0xFF404040 になるのは言うまでもない。