2011-04-30

本は死んだ

The “book” is dead [dive into mark]
JavaScript: The Definitive Guide Sixth Edition pdf download ebook - davidflanagan.com

オライリー JavaScript本(通称サイ本)の作者、David Flanaganのブログに関して、Google社員のMark Pilgrimが反論している。

オライリー JavaScript 第5版の作者である、David Flanaganが書いている

この15年というもの、私は成功した作家の一人であった。自分と家族の食い扶持を、本の印税収入だけで得ることができたのだ。しかし、出版業界は斜陽であり、私の印税収入も、ドットコムバブルの崩壊よりこのかた、下がる一方である。私は、サラリーマンとしての就職口を見つける必要があると結論した。

15年というのは長い。自分の好きなことをそんなに長くやれて、しかも収入があるというのは、実に恵まれている存在と言える。

しかしここに来て、泣きが入って、海賊行為とGoogleについて述べている。(注意:私はGoogleで働いているが、検索部門ではない。私はGoogleのために発言しているのではなく、Googleは私のために発言しているわけでもない。お互いそうやってうまくいっている)

Davidは続ける。

「Googleは海賊行為を助けているのか?」なんてTweetはしたくはない。しかし、思うに正当な疑問ではないだろうか。Googleがebookeeのようなサイトをインデックス化して、ダウンロードサイトへの直リンを貼り、もって目当ての海賊版コンテンツの発見を容易にし、さらにはどのような検索をすべきなのかのサジェストまでしているのであれば、Googleが海賊行為を推奨しているという論調が成り立つのではないだろうか。

結論を、海賊行為を助けているから、推奨しているという方向へ導くのが、あまりにも早計に過ぎるのではなかろうか。多くの技術が、海賊行為を助けている。第一、1か0かの世界なのだから。アナログの世界ですら、図書館は書籍と同じ建物内にコピー機を設置することで、海賊行為を助けていると言える。しかし、図書館は海賊行為を推奨しているのだろうか。否、コピー機の横には大きな張り紙がしてある。著作権法について説明し、参考のために数ページをコピーする以上のことは、高くつくと警告している。技術はデジタルコピーを推奨しているのだろうか。否、単に1か0かのことである。

さらに、Davidの言に半して、ebookeeはDavidの最新の本を持っているわけではない。このサイトは、単にユーザーをして、Usenetやファイル共有サイトのプレミアムなサービスなどに課金させることを目的としている。全世界のあらゆる本の部分的なページを持っているだけである。本当にダウンロードリンクがあるのは、その中のごく一部で、多くは壊れている。大抵、サイトはたらい回しになっているだけなのだ(訳注:目的のブツがあると匂わせて、似たような広告付きサイト間をたらい回しにすること)。このサイトはクソの役にも立たないから、検索から弾くべきであるという主張は成り立つけれども、このサイトが検索結果に存在することを持って、Googleが海賊行為を推奨しているというDavidの意見はあたらない。

さて、Javascript 第6版が出た。まだ私の手元にすら現物がないのに、すでに違法なコピーが出回っている。

それは違う。上記参照。(訳注:実際に検索してみるとわかるが、本物の第6版が出回っているのではなく、フェイクである。単に皆が検索しそうなファイル名を片っ端から機械的に生成しているフェイクサイトが多い)

さて、Googleは違法なダウンロードを、本を検索する全員にサジェストしている(スクリーンショットを参照)

スクリーンショットは、実際のところ、Googleのフィルタープログラムによる海賊用語、たとえば"bittorrent"とか"rapidshare"とか"megaupload"などをフィルターした結果を表示している。もしフィルターしていなければ、サジェストボックスは、海賊用語であふれかえるのである。しかし、重要なことは、海賊用語で溢れかえる理由というのは、皆それを検索するからである。Googleのサジェストは、実際の検索から作られている。それは世界を写す鏡だ。写実的ではあるが、強制されていない結果である。たとえ世界を写した鏡が気に入らなかったとしても、鏡を罵倒しても仕方がない。

Davidは続ける。

Googleは海賊行為をサジェストしないようにフィルターすべきである。

Googleはもうとっくにやってる。上記参照。

Googleはフィルタリングによらない、海賊版コンテンツへのリンクをフラグ付けするべきである。Googleは、「このサイトはあなたのコンピューターに有害な影響を与える可能性があります」というフラグ付けはやっている。なぜ海賊サイトをフラグ付けしないんだ。「このサイトからコンテンツをダウンロードすることは、コピーライトホルダーからの法的措置が取られる可能性があります」とか、「このサイトからのダウンロードは違法である可能性があります」とか、あるいは単純に、「このサイトはあなたの悪業を積み重ねます。」

違いというのは、「このサイトはあなたのコンピューターに有害な影響を与える可能性があります」の判定方法だ。一体、どう実装されているのか、ここで公開されている。疑わしいサイトは、パッチ未適用のブラウザーを走らせた仮想マシン上で確認される。これがインターネットのスケールで動くこと自体が素晴らしいのだ。

ところが、「著作権保護された物」の合法という概念は、インターネットのスケールでは判定が難しい。もちろん、不可能というわけではない。YouTubeは音声と動画に対して、コンテンツIDプログラムを持っている。しかしこれは、YouTubeに直接コンテンツがアップロードされるということを前提にしている。このプログラムをRapidShareに適用させようとすると、Googleは識別のため、RapidShareからのリンクのすべてをダウンロードしなければならない。皮肉なことに、RapidShareは、サードパーティ製のダウンローダーが、広告を回避したり「プレミアム」メンバーシップに加入したりせずして、コンテンツを「盗む」ことを技術的に難しくしている。

これらの技術的な点が解消されたとしても、ebookeeのようなサイトをフラグ付けするのは無意味である。というのも、これらのサイトは、単に違法なコンテンツへのリンクをホストしているだけで、コンテンツそのものはホストしていないのだ。(Googleがフラグ付けし始めたとしても、もうひとつ迂回リンクを経由したり、ダウンロードURLを隠したり、その他のアホ臭い偽装化を行うだけである。海賊行為に、こういった方法で打ち勝つことはできない)

海賊行為に打ち勝つといえば、この匿名コメントだ。

iTunesが成功した理由というのは、ファイル共有と競争したからだ。なんで、iTunesで安く今すぐに落とせるのに、高品質なものがあるかどうかも分からないmp3を探さなきゃならないんだ?

また、Peterは、似たようなコメントをしている。

オライリーサファリをここ数年、購読している。開発者全員におすすめ。一年につき500ドルほど払うのは痛いし、無料の海賊版PDFのような利便さ(オフラインアクセス、コピー不可PDF)がないのもつらいが。

海賊行為が問題なのか? そもそも問題なのか? Davidは自分の本が広く海賊行為にあっているという証拠を一切示していない。実際、海賊サイトにはまだ上がっていないのだ(訳注:新しく出た第6版のことだと思われる)。そもそも根本的な、おそらく不快になるであろう質問というのはこうだ。なんで今どき、わざわざ本など盗まなきゃならないんだ? Stack Overflowとか掲示板とか行けばいいし、Googleに質問を入力して、答えをもらえばいいじゃないか。

Curtと名乗る人物のコメントでこの話を終わらせるとしよう

技術書というのは、実際、非常に読みづらい。しかし、かつては、これが唯一の選択肢だったのだ。今では、私に取って本のような媒体は副次的な選択肢であり、他の選択肢より不便であるし、金もかかる。現代における一番の選択肢は、深くリンクされ、専門特化されたデータなのだ。思うに、海賊行為によって金を失っているわけではない。金を失っている理由は、本という媒体が、そもそも技術データの媒体として、もはやふさわしくないからだ。

Davidもこの意見にはどうしているらしい。Davidの返信もある。

私の本は、本として書かれていて、オンライン上ではあまり役に立たないだろう(金銭的に成功するかどうかは別として)。それが、私の収入の問題になっているのだろう。私は、過去の媒体に対するコンテンツを作成しているのだろう。

「本」は死んだ。「コンテンツ」に栄えあれ。Davidのような世界級の作家でさえ、それで食べていくことができなくなったのだから、神よ我らを助けよ。

これは、実に興味深いことだと思う。今回の人物は、JASRACやMPAAのように、あまりに常識はずれの発言(MP3プレイヤー、一台につき一枚のCDを買え等)をするような団体ではない。

あのDavid Flanaganである。あのオライリーのJavascript本、通称サイ本を書いた、偉大なるDavid Flanaganである。およそJavascriptプログラマーで、彼の名前と本を知らなければモグリの謗りを免れない、あの有名なDavid Flanaganである。あの偉大なる者の意見なのだ。最近、あの有名なサイ本の第6版が出たらしい。それに寄せて、ブログを投稿したのが、この記事で、上記の文は、それに対する反論である。

私も、彼の本を持っている。もちろん、合法的に購入した紙の本、オライリーのサイの表紙絵のJavascript 第5版である。この本がなければ、私はJavascriptを使いこなすことなど到底できなかっただろう。彼の本は完璧である。言語仕様からライブラリー、クロージャーなどの各種テクニック、またDOMをも網羅的に解説している。もちろん、単なる羅列ではなく、ちゃんと作者自身理解して、作者の言葉で解説している。この本を一冊読めば、すぐに二流のJavascriptプログラマーになれるのである。もし、Javascriptの技術書を一冊しか買わないと決めているのであれば、この本をおいて他に選択肢はない。それぐらいすごい本である。

しかし、私はもはや、この本を読まない。なぜかというと、Web上に合法で無料の、Javascriptの言語仕様やライブラリーを網羅したサイトがいくつでもあるし、高度なテクニックの解説も、読み切れないほど存在する。

そもそも、今の私は、C++、Javascript、HTML、CSS、DOMに関して何か疑問が生じた場合、一次ソースたる規格を参照している。これはもっとも手っ取り早い方法である。これらの規格書はできるだけ解釈間違いが生じないように注意深く書かれており、多くの人の手によって検証されている、最も信頼すべき一次ソースである。規格書の英語は、シェイクスピアやKJV聖書より簡単だ。

だから、私は第6版を、買うつもりはない。これも、時代のせいだろう。時代に抗うことはできない。

さて、こんなことを言いながら、私は今、C++0xの技術書を執筆している。ただし、日本はまた、少し状況が違うように思う。とはいっても、未来が明るいわけではない。少し遅れているだけなのだ。

日本人プログラマーの多くは、英語を解さない。ネット上に日本語の文献は、英語に比較して極端に少ないし、情報も劣っている。大抵は英語圏に比べて、数年以上も遅れている。このため、まだ日本には、多少の販売余地がある。しかし、それも、英語圏に比べて少し遅れているというだけで、必然的に、英語圏に追いつくだろう。実際、私の小中高校時代の友人などは、今会ってみると、誰もかれも、大抵英語ができる。私は進学校にいたわけではない。田舎のかなりレベルの低い学校に通っていた。それでも、やはり皆、何かしらの理由によって、英語を必要としている。私の世代では、全員は無理だろうが、次の世代では、日本人とて全員、英語ができて当たり前になっているだろう。そうなったとき、もはや日本語の技術書の市場はなくなってしまう。(まあ、実際は、そこまで日本の未来は明るくないのだろうが)

さて、本当に、技術書の既存の市場の未来はない。おそらく、ここ数十年持つかどうかだろう。もっとも、プログラミングの技術書など、始まってからまだ数十年なので、別に天地がひっくり返るようなものでもない。もともと歴史が数十年なのだから、後、数十年、いやもっと短い期間しかもたなかったとしても、別に驚きではない。

事実、最近の参考書の売上は、あまりかんばしくないと聞く。日本では、技術書の海賊版というのは、あまり行われていない。たとえば、David FlanaganのJavascript 第5版の英語の原書であれば、ググればそれらしきPDFがそのままトップに引っかかる。ところが、日本語版のPDFというのは、実際に探してみたが、全く見つからない。したがって、日本における現行の紙の技術書の、海賊行為の影響はまずないといっていいだろう。そもそも、日本の出版社の多くは、電子媒体を提供していないのだ。だから、海賊版がだせるとすれば、いわゆる自炊と呼ばれる裁断、スキャン、連番画像になるのだ。漫画ならともかく、文章を主体とする技術書に、そんな不自由なフォーマットで満足できるわけがない。と思いたいが、実際に、もちろん合法的に自分で購入もしくは入手した書籍を使って、自炊しているプログラマーが何人かいるらしい。にわかに信じられないことである。

さて、その未来において、いったい技術書の執筆で、どのようにして利益を得られるのか。やはり、対抗するしかないだろう。間違ったやり方は、アホ臭いDRMをかけたり、特定のプラットフォーム、特定のデバイスに限定して、囲い込むことである。そんなやり方は、うまくいくわけがない。正しいやり方は、海賊版と同じ利便性を提供するのである。

そういうわけで、今執筆中のC++0xの参考書も、前々から計画はあるわけだ。まだ時期尚早だが、読者は、現時点で最もオープンでDRMフリーなフォーマットの電子書籍を購入できると期待してよい。もちろん、多くのプラットフォームで実装されており、文章は全文検索でき、リキッドレイアウトであり、ユーザー指定のスタイルに変更可能であり、スクリプトを走らせることができ、また、万が一、このフォーマットに対応していないプラットフォームのためのフォーマットの変換も容易である。そんなフォーマットによる電子書籍を期待してよい。

フォーマットが具体的に何なのか。聡明な読者はもう察しがつくことであろう。

これを実現するためには、色々と障害物が多く、また、痛みを伴うかも知れないが、誰かが先陣を切ってやらなければならないのだ。あの精神錯乱を起こしたニーチェのように。

追記:David Flanaganのブログを読めば分かるように、Davidは、ある作者は、合法的に無料でダウンロードできる本で儲けたことを知っているし、またあるフィクション作家は、99セントの本を自費出版して儲けたこともしっている。しかし、それは、一部の幸運な作者が、3.99ドルで売った時より儲かったというだけの話である。そんな一部の幸運な作者が、99セントで自費出版するような世界が、技術書の未来であるわけがない。第6版を書くのには、フルタイムで働いて一年以上かかったのだから、と言っている。

Tweet Buttonが機能していない

ここ数日、Tweet Buttonによる検索が機能していない。何故だろう。

投稿に対するtweet数は表示されるのだが、どのような投稿をしているか見てみようと思っても、検索が動かない。不思議だ。このカウント表示は、ブログを重くさせるだけだから、しばらく止めてみようか。

止めても、パフォーマンスは向上しなかった。理由は、このTweetボタンの実装は、何を考えているのか、iframeなのだ。たった画像一枚表示するだけなのにiframeを使っているとは。投稿ごとに対応するTweetボタンを表示しようとすると、それだけiframeを追加しなければならない。それで、パフォーマンスが悪くなる。

iframeを使わない方法はないだろうか。JSONPで必要な情報を取得できればいいのだが。たぶん、widget.jsがそれをやっているのだろうが、肝心の情報が公開されていない。公開されていないということは、将来変更される可能性がある。そんなものは使いたくない。

Twitterの公式検索はJSONPによるAPIを提供しているので使おうかと思ったが、現在のところ、古い投稿は検索できない。これでは、Tweet数として表示しても意味がないので、やめることにした。単に静的なリンクにとどめた。結局、Tweet数というのは必要ないだろう。パフォーマンスを大幅に落としてまでやることではない。

ところで、はてなブックマークの対応は、果たして必要かどうか、疑問になってきた。というのも、もはやオンラインブックマークは、斜陽だと思うのだ。これは、独りはてなブックマークに限らない。diggも、もはやそのかつての勢いはない。すべて、Twitterにユーザーを吸い取られてしまったようだ。

Twitterは、オンラインブックマークのように、全ユーザーのコメントを追うには適していないが、圧倒的なユーザー数がある。これは当然だと思う。というのも、わざわざオンラインブックマークをするのはめんどうだからだ。ただし、Tweetは簡単であり、TweetにURLを含めるのは、自然である。これこそが、Twitterがオンラインブックマークを駆逐しつつある理由だろう。

とはいえ、ワシはまだ、Twitterを認めて遣わさぬぞ。ブログこそが最も優れているのであり、あのようなMicro Bloggingは、邪道じゃ。何が悪いかといって、ブログならばまとまっていたはずの重要な情報が細切れになってしまうことじゃ。世も澆季じゃて。

2011-04-29

Boost.localeが変更された

charの文字列を受け取るtranslateに、エンコードを指定できるようになった。wchar_tの文字列を受け取るwtranslateが追加された。

疑問としては、char16_tとchar32_tをとるtranslateがないのはなぜだろう。また、エンコードは、 "windows-936"などのように、文字列で指定しなければならないらしい。何故文字列なのだろうか。enumではダメなのか。

2011-04-28

Translation is impossible

This is one of planning posts explaining about the situation of Japan and programming. This time, I would like to write about non technical matters. How bad the translation is.

Last time, I explained Why Japanese don't know English. Short answer is: We don't need to because there are enough translations.

So It's all about translation. Is it good? Short answer, No. Long answer, NOOOOOOOOOOOOOOOOO.

I say, translation is enough to be a good programmer. You can't be the best programmer by translation.

The translation, in general, is horrible.

The author of Boost.locale said by using English, It's easy to translate to other languages.

You even do not have to be a professional tanslator with a degree in Lingustics to translate messages from English to your own language.

This is the last thing localization library designer can say.

Translation is a bad idea. Translation is a workaround. It's works as lossy encoding like using 32kbps mp3. It just barely sufficient to grasp the meaning of original text. All non-essential nuances are removed.

Especially, translation between English and Japanese are nightmare.

That's not all. You can't avoid mistranslation.

I'll show you one example. One example that is enough to makes you understand that translation never works. The most infamous mistranslation in the history of computer science. Brace yourself.

There is a book called "The Programming Language C++ 3rd" by Bjarne Stroustrup. Of course, There is a Japanese translated version for this. At 4.4.1 Integer Literals [dcl.int.lit] of this book, there is a sentence:

on a machine on which an int is represented as a two’s complement 16-bit integer

This was translated in Japanese as follows.

2つの補い合う16ビット整数でintを表現するマシンでは

(Literally translated back to English by me.)

on a machine on which two 16-bit integers that are complementing each others represents an int

I'm not joking. I don't make up this. I tried to translate as literal as possible. It's real. It literally says "two 16-bit integers". This set of integers are somehow complementing each others. And it somehow represents an int. Yes this two integers represents an int. not "be represented".

This legendary horribly translated version of his book is still sold even today. You can find it in Japanese local bookstore.

Of course, there are books written by native Japanese. But for some reason, it only covers the basics. So we have to read translation.

This problem can be solved if we all learn English. But that's impossible. I think the reason of that is a good topic for next post.

South Parkのシーズン15が開始

HUMANCENTiPAD (Season 15, Episode 1) - Full Episode Player - South Park Studios

誰も許諾文なんて読まないというネタ。

Japanese programmers don't know English

"Japanese programmers don't know English."

This is a fact. Sure, there are few exceptions. But I can safely say almost all Japanese programmers don't know English.

Why is it? Why we don't know English? How could that even be possible? How can we learn programming without knowing any English.

Why? Because we don't need to. How? Because there are enough Japanese translations.

In the world of programming, English is the first class citizen. Any other languages are secondary. I don't argue with that. It's a fact.

But we have translations. Almost all English programming books are translated to Japanese. It's not just books. Standards, documents and every interesting text in the internet are translated to Japanese. If you google for it. There is a high chance it has been already translated by man(not machine).

Because of this, we don't need to learn English just to learn programming. Of course, if one want to be the best programmer, he or she still have to learn English. But you don't need to be the best programmer. Actually, you can't be the best programmer. It's a simple fact that you can't master all. There is always better programmer than you for some domains. What we need is good programmers. Because we have enough Japanese translations, we can learn most of the stuff in Japanese.

Now, you may wonder like this. "But programming language is based on English. Writing a code means writing an English. same applies for reading. isn't it?"

That's not what we do. We are dealing with programming language, not English. When we read fopen, we don't think "It's a function which open a file". We think ファイルを開く関数(a function which opens a file). We don't recognize f stands for file and open means, well "open". We think "fopen" as some kind of identifier and whatever that means it opens a file in Japanese. Likewise, when we write variable name such as file, we don't recognize it as "file". We think ファイル(file).

It's just same you guys use Japanese words like samurai, ninja and hentai without understanding its true meaning. Just because you use these words doesn't mean you understand Japanese. You don't even know what it really means. Most samurais were doing boring jobs that is equivalent to current office job. Reading, Writing and computing. Ninja doesn't ware black clothes and they weren't super athletes. They were spys. Their job was same with modern spy. Obtaining informations and propaganda. Hentai does NOT mean porn anime and manga. It's more generic word. It may be translated to naughty or dirty.

Japan is a interesting country. In 1867, Edo bakufu(a feudal government at that time, they banned forign culture Sakoku) gave up and returned its rights to the Tenno(They got rights to govern the Japan in 1603 from Tenno.) After that, we were eager to learn western knowledge. We built many western style buildings made by bricks(most western style buildings were destoryed by earthquake, notably 1923 Great Kantō earthquake. Earthquake is the reason our ancestor never built a house made by brick.)

Well, enough of history. The point is, many western technologies were far superior than ours at that time. We had to learn fast to keep up with them. Or else, we ended up to be invaded and become a colony of western countries. We had to avoid that. The only way to prevent that is learn.

In order to learn western knowledge, it's obvious we need to read western languages. Of course, the smartest people learned it. The most interesting thing is they translated it to Japanese. So all Japanese could read it. Did you know that the Japan's literacy rate was really high even at that time. I think we made so many translation because of the high literacy rate. If literacy rate was low, we didn't need to translate it to our language.

Even today, we translate most of the forign books. I think the biggest reason Japanese can't use English is, just because we don't need to. We have translations. That's enough for becoming a good programmer.

I don't know how long we can keep doing this. We need a common language and English is the best candidate whether we like it or not. Eventually, minor languages will slowly fade away. I think Japanese language survives. Well at least I hope so.

2011-04-27

Boost.localeは何の冗談だ

The problem even if the source is UTF-8 with BOM "שלום" would
be encoded according to locale's 8bit codepage like 1255 or 936
and not UTF-8 string (codepage 65001).

It is rather stupid, but this is how MSVC works or understands
the place of UTF-8 in this world.

Unicode and Visual Studio is just broken...

こんな人間がよくlocaleライブラリなんて設計しようと思い立ったな。こう言ってやりたいね。

Give us all a favor and cease developing locale library. It's not only useless, but also harmful. It encourage people to use ASCII.

2011-04-26

IE9を使ってはならない

IE9はとんだ嘘つきブラウザーだ。

続pre要素の改行

preの仕様だが、HTML5の仕様をよく読むと、"in the HTML syntax"と書いてあった。ということは、これはXHTMLには当てはまらないことになる。

私は、常にXHTMLを使っているので、とんだ勘違いをしてしまったのだ。

道理で、他のブラウザーでは結果が違うわけだ。他のブラウザーは正しくXHTMLを実装しているのに対し、IE9はXHTMLを正しく実装していない。

IE9が間違っているのだ。XHTMLにおいては、pre要素の直後の改行要素を取り除いてはならない。

なんと間抜けな間違いをしてしまったのだ。IE9だけが間違っているのだ。XHTMLをサポートしているなどとどうして言えたものか。

IE9のオプションが最高に分かりにくい件について

IE9には、以下のようなオプションがある。

☑GPU レンダリングではなく、ソフトウェア レンダリングを使用する*

これにチェックを付けると、GPUレンダリングを使わない。なんて分かりにくい文章だ。最初にGPUレンダリングという言葉があるため、これにチェックを付けるとGPUレンダリングを有効にするのかと思ってしまった。チェックボックスに否定的な文が入っているのは最悪である。一般ユーザーにブール代数と複雑な自然言語の解釈を要求してはならない。そもそも、一般ユーザーはGPUレンダリングとソフトウェアレンダリングの違いが分からないのだから、尚更、この文章の意味は分かりにくいものとなる。

チェックボックスのテキストに、こんなに複雑な文章を使うべきではない。むしろ「☑GPUレンダリングの有効化」みたいに、なっているべきなのだ。

ちなみにこのGPUレンダリングを一般ユーザーに有効化させるのは、結構面倒である。というのも基本的に、一般ユーザーは、ドライバーのアップデートをしないものである。しかし、GPUのドライバーというものは、かなりアップデートが頻繁なのである。これは、最近のGPUは複雑で、バグフィクスが頻繁にあるからだ。また新機能にもドライバーのアップデートだけで対応できる場合もある。そのため、GPUのドライバーはしょっちゅうアップデートされる。

コアなゲーマーならばともかく、一般ユーザーにGPUドライバーのアップデートができるものだろうか。

IE9が期待以上に素晴らしい件について

IE9をしばらく試してみたが、期待以上に素晴らしい。

いくつかのHTML5の実装に関しては、どのブラウザーより進んでいる。XHTMLにも、当然対応している。Javascriptも、現行のブラウザーと遜色ないほど早い。DOMのサポートも素晴らしい。addEventListenerは当然として、その他の最近主流の提案は、他のブラウザと遜色ないほど実装されている。

UIなどはともかく、規格準拠という点においては、現行の主要なブラウザーと遜色が無い。

IE8以前のブラウザーを使っているのならば、すぐにアップグレードするべきである。もし、XPを使っているのならば、OSをアップグレードするべきである。

さて、実際の使い勝手は、私が常用しているChromeから乗り換えるほどではない。確かに、規格的には、現行のどのブラウザーより優れている。ただし、一つ重要なブラウザーとしての機能が欠けている。User scriptである。

FirefoxにはGreasemonkyがある。Chromeはextension機構がブラウザー自身に用意してある。Operaも確か、単純なuser scriptぐらいなら設定できたはずだ。Safariにも、似たような機構があるようだ。

私はChromeで多くの自作のextensionを使っている。これは、基本的には、JavascriptによるDOMいじりとCSS注入をしているだけなので、IE9でも、そのような機構さえ備わっていれば、簡単に移植できる。ただし、IE9にはそのような機構がない。Bookmarkletは、お世辞にも使いやすいとは言えない。

結果として、私は今回、ChromeからIE9に移行することはないだろう。結局、まともなブラウザーがひとつ増えただけだ。

preの改行文字の除去について

HTML5では、pre要素の直後の改行文字は、HTMLの文法レベルで取り除くこととなっている。

つまり、以下のようなマークアップがあったとして

<pre id='test'>
a
</pre>

document.getElementById("test").textContentは、"a\n"であるべきだ。なぜならば、HTMLの文法上、改行文字が取り除かれるのだから。

Chrome、Opera、Safari、Firefoxは、"\na\n"だった。これはHTML5の仕様に違反する。IEは、"a\n"だった。これは正しい挙動だ。

現状では、IE9以外は、pre要素を正しく実装していない。

IE9は正しくXHTMLを実装していなかった。

追記:innerTextではなく、textContentを使うべきである。

IE9日本語版が公開された

Internet Explorer のダウンロード - Microsoft Windows

さっそく試してみたところ、HTML5のサポートに関しては、現行のブラウザでも最高レベルである。

たとえば、pre elementだ。HTML5では、preタグに直ちに続く改行文字は、取り除かれると規定されている。

Note: In the HTML syntax, a leading newline character immediately following the pre element start tag is stripped.

HTML5 : The pre element

つまり、以下のようなHTML5のマークアップに対しては(改行文字も含む)

<pre>
text
</pre>

以下のように描画されるのが正しい。ただし、ここでは他のブラウザでも表示を一致させるため、preを用いていない。また、分り易くするために、ボーダーを追加している。

text

ところが、多くのブラウザでは、HTML5を実装していないがために、以下のように描画される。


text

これは正しくない実装である。

しかし、</pre>の直前の改行文字まで取り除かれているようなのだが、これはどうなのだろう。

ところで、IE9日本語版の正式リリースを記念して、例の仕掛けの文章を書き換えておいた。もちろん、だいぶ前から、IE9以降では、例の仕掛けは発動しないようにしているのだが。

2011-04-24

Overload ResolutionとPartial Orderingの説明が面倒だ

Overload ResolutionとPartial Orderingの説明が面倒だ。果たして、本当に詳しく説明する必要があるのだろうか。詳しく説明するというのは、実際のルールを細かく説明するという意味である。

何が面倒かというと、ルールが実に多いからだ。ポインターの場合は、ポインターにCV qualifierがつく場合は、リファレンスの場合は、リファレンスにCV qualifierがつく場合は、lvalueとrvalueリファレンスの違いについてなど、あまりに説明しなければならない事項が多すぎる。

仮に説明したとしても、これらの細かい規則を覚えておくことは、実際のプログラミングにおいて、何の役にも立たない。何故ならば、これらの細々とした規則は、より人間に取って自然になるように、20年ほど議論された結果だからだ。ルールを知らなくても、人間に取って自然に動くように設計されている。(もちろん、時として意外な結果をもたらすことはあるが)

参考書を書きたいと思った動機の一つに、これらのルールを細かく説明したいということもあった。しかし、実際に説明しようと思ってみると、どうも規格の文面の翻訳以外の言葉が思いつかない。翻訳というのは危険であり、頼ってはならない。規格を翻訳するぐらいなら、最初から規格を読めばいいのだ。

C++ Templatesですら、Overload ResolutionやPartial Orderingは、概要ぐらいしか説明していない。結局、細かく説明しても意味がないからだろう。

やはり、このふたつは、概要の説明ぐらいにとどめたほうがいいのかもしれない。

Boost.Localeが採択された

Boost.Localeが、賛成10、反対5で採択された。たったの15人しかレビューしていないライブラリーが、万人の役に立つと、どうしていえようか。しかも、15人のうち、東アジア人としてレビューしたのは、私一人なのだ。まあ、役に立たないライブラリがひとつ増えるだけだ。

localeによる翻訳に対する、ユニークな識別子としての入力には、ASCIIを渡さなければならない。何故ならば、このライブラリの作者は、英語こそが至高の言語であり、全プログラマーとソフトウェア開発に携わる全人間は、当然のごとく英語を使うべきであると信じているからだ。何故ならば、およそ日本語のような言語は、理解するものが少なく、他の言語に翻訳しにくい。英語こそが全世界の共通語であり、英語で書かれていたならば、各国語への翻訳は簡単だからだというのである。

そのため、翻訳文へのマッピングにはASCIIを渡さなければならないのだ。

std::cout << translate("Use the goddamn English! You silly Japs! Stop using the Moon language!") << std::endl ;

しかし、ソフトウェアにおける文章とは、単にプログラマーだけが書くものではない。ゆえに、このライブラリを使おうとすると、ソフトウェア開発に携わるすべての人員に、十分な英語の知識が必要である。しかし、そんなに末端まで英語に堪能な人材を集められるとするならば、そもそもそんな最初から翻訳など必要ないということになってしまう。

ましてや、今日の英語だって、ASCIIの範囲内の文字だけでは表現できない。ということは、このライブラリーを使うには、ふたつの英語のテキストを管理しなければならない。本物の英語のテキストと、translate()に渡すユニークな識別子としての英語のテキストである。

最近、Boostには、大多数のプログラマーには役に立たないライブラリが多すぎるように思う。本当に必要なライブラリーは、大多数のプログラマーの意見の不一致により否決され、Localeのように、現実には何の役にも立たないライブラリーが、あまり注目されないために、まともに議論もされずに通ってしまう。

Boostの査読によるライブラリ採択の仕組みは、もはや完全に破綻しているのではないだろうか。

2011-04-19

C++0xのUTF-8対応に問題あり

今まであまり気にしていなかったのだが、C++0xのUTF-8対応には、非常に深刻な問題があるように思われる。

C++0xでは、u8 encoding prefixを使うことによって、UTF-8でエンコードされた文字列リテラルが使える。

u8"あいうえお" ;

しかし、このリテラルの型は、char const [16]なのだ。(UTF-8では、ひらがなは一文字3バイトを要する。null終端を付け加えて、サイズは16となる。)

しかし、charという型は、歴史的に、あらゆるマルチバイト文字コードを格納するのに使われている。charを入力に受け取った時点で、それがどんなエンコードを使っているかは分からないのだ。

つまり、以下の様な関数を書いた場合、

// UTF-8の文字列を入力に取る関数
void f( char const * ptr )
{
// ptrがUTF-8文字列を参照している保証はない
}

ptrの指し示すNULL終端文字列は、UTF-8でエンコードされているかもしれないし、それ以外の実装依存のエンコードが使われているかもしれない。問題は、関数のユーザーに、UTF-8を強制させる方法はないということだ。ユーザーはencoding prefixのない素の文字列リテラルを渡すかもしれない。あるいは、仮にUTF-8文字列リテラルを使う意図があったとしても、u8プレフィクスを付け忘れるかもしれない。

f(u8"あいうえお") ; // 正しいリテラル、UTF-8エンコードが使われる
f(u"あいうえお") ; // コンパイルエラー、型が違う
f(U"あいうえお") ; // コンパイルエラー、型が違う
// u8を付けるのを忘れた
f("あいうえお") ; // 実行時エラーになるかもしれない、実装依存のエンコードが使われる

たとえ、ユーザーがタイプミスを犯したとしても、このエラーをコンパイル時に検出することはできない。実行時でも、入力が本当に妥当なUTF-8かどうかを検証するのは、容易ではない。第一、たまたま妥当なUTF-8の文字列として解釈できるマルチバイト文字コードのバイナリ列など、いくらでもあるだろう。単にassertを使うこともできない。デバッグは、手動で行うか、あるいはかなり高度なヒューリスティックな手法を使うものになるだろう。あまりよろしくない。

とはいえ、これまでもUTF-8の受け皿としてchar型が使われてきたことも事実だ。なかなか難しい。

もちろん、別の型があるからといって、壊れた入力がなくなるわけではない。ただ、u8を書き忘れたなどというような、初歩的なミスをコンパイル時に発見できるだけである。

どうも、C++0xのFDISに、素直に賛成できなくなってきた。後はユーザー定義リテラルか。

Boost.Localeがクソすぎる

Boost.Localeがレビューされているので見てみたが、クソすぎる。しかも、作者はそれが糞であることに気がついていない。

Boost.Locale: Boost.Locale

Boost.Localeはstd::localeの機能を持っている。しかし、日本人なら誰でも知るように、std::localeはクソの役にも立たない。よって、Boost.Localeも、その機能としては役立たずだ。

Boost.Locale: CollationBoost.Locale: Conversionsでは、大文字、小文字、アクセント記号の有無に対する無視や、相互変換などの機能を提供している。これは、日本語には何の役にも立たない機能である。

Boost.Locale: Numbers, Time and Currency formatting and parsing

これは、数値や日付、貨幣単位に対する変換である。たとえば、123456789という数値を、それぞれのロケールに合わせて、123,456,789(3桁区切り)にしたり、あるいは1,2345,6789(4桁区切り)にしたりできる。また、時刻を、これまたそれぞれのロケールに合わせて、文字列で返すことができる。また、通貨単位も付加できる。

これは、ポルポが食べていたクラッカーの歯クソほどの事もない機能である。あるひとつのロケールに置いてすら、数値や日付のフォーマットは様々あり、単にひとつのフォーマットに置き換えるなど、何の役にも立たない、ましてや、Jan 1st 2011とか、1月 1日 2011になったところで、何の役にも立たない。そもそも、日本語はどうするのだ。バックスラッシュ(日本語フォントでは半角¥)を使うのか? ¥(U+FFE5: FULLWIDTH YEN SIGN)を使うのか?

まあ、これは何の役にも立たないから無視してよい。次。

Boost.Locale: Messages Formatting (Translation)

これは、翻訳である。unixのgettextをモデルにしている。これは最悪である。原理としては、各国語のマッピングを持っておき("hello"と"こんにちは")、それを使うのだ。これは、英語から日本語への翻訳には、ある程度有効だが、日本語から英語への翻訳には絶望的に適していない。実は、私は昔、あるgettextを用いたあるソフトウェアの翻訳をしたことがある。まさか、その知識がここで生きるとは思わなかった。

std::cout << translate("hello") << std::endl ;

translateが引数として受け付けるのは、char const *かstd::stringである。wchar_tは受け付けない。この理由を作者に問いただしたところ、信じられないような答えが返ってきた。

「あらゆる言語は英語を基調とすべきであり、それプログラマーたる者は、すべからく英語を使うべし。まず英語で書き、つぎに各国語に翻訳するのだ。これこそがunixのgettextの思想であり、また本ライブラリーの目的でもある」

これほど現実を無視した馬鹿げたことはない。入力にcharを使うということは、NULL終端されたあらゆるバイナリ列が渡されるということだ。

たとえば、MSVCにおいて、translate("あ")とtranslate("궇")は同じバイナリ列である。前者はシフトJISを使い、後者は、おそらくKS X 1001という韓国語の文字コードを使うはずである。

作者はどういうわけか、ソースファイルをBOM付きUTF-8にすれば、MSVCはencoding prefixなしの文字列にUTF-8を使うと信じている。とんでもない誤りである。MSVCは、現在知られているあらゆる文字コードに対応し、encoding prefixなしの文字列に対しては、自動的に対応するMBCSの文字コードに変換をしてくれるのだ。作者が何故このような誤った事を信じているかというと、おそらく、UTF-8はASCII互換だからなのだろう。このことは、何度説明しようとも、作者は盲信を改めようとはしなかった。典型的だ。

また、このライブラリーはcharの入力としてASCIIのみを期待しているので、別の文字コードを渡すのはプログラマーが悪いとのことである。現実を全く見ていない。そもそも、charのエンコードはASCIIだと保証されているわけでもないのに。

作者は、wchar_tはコンパイラーによって実装がまちまちであり、ポータブルなコードでは使うべきではないという。それを言えば、charだってエンコードはまちまちであり、使うべきではないのだ。なぜC++0xでは、UTF-8用のchar8_tのような型を用意しなかったのか。

普通、日本人がこのライブラリーの噂を聞けば、これは対応する翻訳文へのマッピングを行ってくれるライブラリーであり、当然、日本語から英語へのマッピングをサポートしているだろうと期待するだろう。しかし、実際にドキュメントを読んでみると、「英語以外の言語は二級市民である。日本語は二級市民である。お前は英語を使わなければならない。このライブラリーは英語から各国語へのマッピングをするのであって、お前の言語から多言語へのマッピングはしない」とあれば、さて、どうなることやら。

日本人プログラマーはほぼ全員、英語が使えない。書けない話せないだけではなく、読むことすらできないのだ。日本人にとって、「プログラミングには英語を使うべし」というのは、クリンゴン語エルフ語ヒュムノス語を使えというのと同義である。(おそらく、日本人プログラマーは全員オタクであると想定して差し支えないだろうから、このうちでもっとも可能性のあるのはヒュムノス語である)

さらに、作者は、Unicodeでは、ワイド文字列を使わなければならないというのは都市伝説(Myth)であり、実際にはそのようなことはない、UTF-16は最適なエンコード方式ではないなどと、意味不明のドキュメントまで書いている始末。

Boost.Locale: Recommendations and Myths

我々はワイド文字列を使う。我々はUTF-16を使う。確かに、UTF-16は最適のエンコード方式ではないが、UTF-8だって最適ではない。これを単に都市伝説として扱うというのは憤懣やるかたない。

さらに、pluralへの対応は汚すぎる。pluralとは、英語で例を上げれば、I have 1 file. I have 2 files. というように、単数と複数に応じて名詞にsがつくものをいうのだ。pluralはソフトウェアで対応すべきではないのだ。あのマイクロソフトが、その気になれば数千人の翻訳作業用の人員を簡単に雇えるほどの資金力を持つマイクロソフトが、Windowsではpluralを正しく扱っていない(I have 2 file(s).)ことを考えれば、そもそも自然言語の方を合わせるべきである。

ゆえに、このライブラリーはrejectすべきである。もし採用するのならば、ドキュメントで明確に、日本語とMSVCとWindowsのサポートを否定すべきである。

数日前、このような内容のメールをBoostのMLに投げつけたのだが、作者は一向に問題意識を持たない。このライブラリーがどうなるにせよ、我々日本人には関係のないことだ。

2011-04-14

ムーブ後のオブジェクトの状態について

n3264: Moved-from state

n3264を理解出来ない人がいるようなので解説。n3264では、ふたつの異なる種類の型のオブジェクトに対する、ムーブ後の保証を規定している。標準ライブラリで定義されている型と、標準ライブラリに渡されるユーザー定義の型である。

ムーブ後のオブジェクトの状態がどうなるかということは、興味深い問題である。根本的には、プログラマーの好きにすればよい。しかし、標準ライブラリと、標準ライブラリに渡される型においては、挙動を決めて置かなければならない。さもなければ、信頼できるコードが書けない。そのため、n3264で、ムーブされた後のオブジェクトの状態が定められた。

17.3.24 [defns.valid.unspecified]には、標準ライブラリで定義されている型のオブジェクトが満たすべき保証が示されている。

標準ライブラリで定義されている型のオブジェクトは、別に明記されていない限り、妥当だが未規定の状態になる。

これは、破棄や代入などの操作が、通常通り行えるということを意味する。ただし、未規定の状態ということに注意する必要がある。たとえば、ムーブ後のstd::vectorのオブジェクトのsize()などがどのような値を返すかは分からないということである。

int main()
{
    std::vector<int> v = { 1, 2, 3 } ;
    std::vector<int> m = std::move(v) ;
    v.size() ; // 未規定
}

Table 34と36で規定されているのは、ユーザー定義の型が、MoveConstructibleやMoveAssignableを満たす際に必要な条件である。これは、17.3.24 [defns.valid.unspecified]とは別ものである。この2つの要求を満たす際の保証ということになる。標準ライブラリで定義されている型の保証より、かなりゆるい。

こちらの保証の意味は、ムーブ後もライブラリが要求する他の保証を満たすこと、というものである。例えば、ある標準ライブラリが、MoveConstructibleかつDestructableを要求した場合、ムーブ後のオブジェクトは、破棄可能でなければならない。もし、CopyAssignableも同時に要求していたのならば、ムーブ後のオブジェクトは代入可能でなければならない。

標準ライブラリで定義されている型は、強い保証を求められる。一方、ユーザー定義の型は、最低限の保証を求められる。

post-Madrid mailingの簡易レビュー

post-Madrid mailingが公開された。

最新の現行ドラフト(Working Draft)はn3291。ただし、今回はFDISがでている。FDISはn3290となる。現行ドラフトとの違いは、変更点の差分表示がされていないということである。

Core Issue 355: Global-scope :: in elaborated-type-specifier

core issue 355に対する修正。規格の定義に従えば、struct ::A { } ;はill-formedであるが、現行のコンパイラーはすべてサポートしていることや、特に問題が見当たらないことを鑑みて、許可しようという変更。変更内容が膨大なため、ペーパーを作成した。

N3260: Consolidated corrections for a cluster of constexpr concerns

定数式に対する細かい変更。といっても、意味としては、だいぶ大きな変更になる。

core issue 1197 を解決する形で、constexprの配列を許可した。例えば、以下の様なことが可能だ。

constexpr int a[] = { 1, 2 } ;
constexpr int b = a[0] ;

以前は、配列の一部の操作が定数式とは認められなかったので、これができなかった。

core issue 1060も解決。以下の様なコードが、ill-formedになる。以前は不思議なことにwell-formedだった。

enum struct E { e = 1 ; } ;
int a[ E::e ] ; // FDIS以前はwell-formedだった

core issue 1100を解決。Integral constant expressionでは、conversion functionは一つでなければならない。コードで説明したほうが分かりやすい。

struct X
{
    constexpr X() { } 
    constexpr operator int() { return 1 ; } 
    constexpr operator long() { return 1 ; }
} ;

int main()
{
    constexpr X x ;
    constexpr int i = x ; // OK
    int a[x] ; // エラー、曖昧
}

N3262: Additional Core Language Issue Resolutions for Madrid

その他のFDISで修正されたcore issueリスト。

N3263 - More on noexcept for the Containers Library (revision)

コンテナーライブラリーでnoexcept指定するメンバーを追加。

n3264: Moved-from state

ムーブされた後のオブジェクトの状態を規定。標準ライブラリのオブジェクトがムーブされた後は基本的に、valid but unspecified stateになる。これは、オブジェクトの状態は規定されていないが、その型に対して規定されている操作はすべて行えるということを意味する。

たとえば、std::vectorのムーブ後のオブジェクトに対して、empty()を呼び出すことはできる。ただし、状態が規定されていないので、どのような値を返すのかは分からない。その他の操作も、すべて規定されているように動作する。もし、empty()がtrueだった場合、front()は使えないなどだ。

これに対し、MoveConstructibleやMoveAssignable要求を満たす型は、もう少しゆるい保証となる。なぜならば、これは標準ライブラリ外のユーザー定義の型にも適用されるからだ。たとえば、std::vectorにムーブ可能な型を要素として渡して、実際に要素のムーブを行う場合、その型はこれらの要求で定義される保証を満たさなければならない。

このふたつの要求で保証しなければならない動作は、ムーブ後の状態は未規定で構わないが、標準ライブラリが、その要求と共に指定する他の要求は、依然として満たさなければならないというものだ。

たとえば、標準ライブラリに渡す、ある型がMoveConstructibleかつDestructibleを満たさなければならないと規定されていた場合、その型のオブジェクトはムーブ後も履き可能でなければならない。もし仮に、Destructibleが指定されていない場合は、ムーブ後のオブジェクトは破棄できなくてもかまわない。なぜならば、Destructibleが要求されていないということは、標準ライブラリはオブジェクトの破棄を行わないからだ。その場合、破棄はユーザー側の責任であって、標準ライブラリ側の責任ではないからだ。

n3266: Revision 2 of: Proposed Resolution for CH 15: Double check copy and move semantics of classes due to new rules for default move constructors and assignment operators

ムーブコンストラクターとムーブ代入演算子のデフォルトの意味が変わったので、いくつかの標準ライブラリの挙動も変わってしまう。それに合わせた変更。

N3267 - A review of noexcept in the threads library

スレッド周りのライブラリーで、noexcept指定できるものはnoexcept指定する変更。

n3268: static_assert and list-initialization in constexpr functions

constexprが大幅に改良された。これまでは、constexpr関数の本体は、たったひとつのreturn文で構成されていなければならないとされていた。しかし、これはあまりにも制限が強すぎるので、いくつかの文が許可された。以下のように書くことができるようになった。

constexpr int f()
{
   ; // null statement
   static_assert(true) ; // static_assert-declaration
   typedef int type1 ; // typedef delcaration and alias-declaration
   using type2 = int ; // that do not define the classes or enumerations.
   using std::size_t ; // using declaration.
   using namespace std ; // using directive

   return 0 ; // and exactly one return statement.
}

return文をひとつだけしか書けないというのは、変わっていない。constexprコンストラクターも、return文がないという事を除けば、同じように制限が緩められる。

N3269: shared_future(future<R>&&) should be allowed to throw

名前のとおり、shared_future(future<R>&&)は例外を投げられるべきだという変更。

n3270: Variadic Templates: Wording for Core Issues 778, 1182, and 1183.

Variadic templatesでどうもあやふやだった部分を明確にした。

以下の様なコードがwell-formedになる。

template<class ...T> struct value_holder {
// Values is a non-type template parameter pack and a pack expansion
    template<T ...Values> apply { }; 
  };

以下はエラーになる。

// Error: Values expands template type parameter pack T within the same template parameter list
template <class ...T, T ...Values> struct static_array; 

n3271: Wording for Range-Based For Loop (Option #5)

range-based forでクラス型のイテレーターを得る際、まずメンバー関数begin()/end()を探し、見つからない場合は、std名前空間をassociated namespaceに付け加えたADLによる検索に切り替える。これは、オプション5と呼ばれていた提案である。

N3272: Follow-up on override control

class member name checkingから、hidingとexplicitを取り除く変更。名前を隠すことに対するチェックと、明示的なhidingとoverrideの修飾を要求する機能がなくなる。結果として、finalとoverrideだけが残る。

hidingとexplicitは、コンセンサスが得られなかったのだ。まだ時期尚早ということだ。惜しい気もするが、仕方がない。

n3276: US22/DE9 Revisited: Decltype and Call Expressions

decltypeの未評価オペランドにおける関数呼び出しの結果の型に、不完全型を許すようにした。これにより、以下の様なコードが書ける。

struct unique_type ; // 定義しない
unique_type f() ; // 定義しない

std::is_same< decltype( f() ), unique_type >::value ;

この例では、定義が必要ないのだ。なぜ不完全型が許可されていなかったかというと、そもそも未評価オペランドは、sizeof向けの定義だったので、当然、不完全型は許可されていなかったのだ。decltypeで、sizeof用の定義を使いまわしたのが問題だった。

N3277: Core issues 1194/1195/1199: References and constexpr

リファレンス型をリテラル型にする変更。これまでは、constexprで、仮引数や戻り値の型にリファレンスを許可するという目的で、いちいち個別に許可していたのだが、そもそもリファレンス自体をリテラル型にしてもいいじゃないかという発想から、このような変更に至った。

n3278: Recent Concurrency Issue Resolutions

concurrency周りの雑多な問題の解決。

n3279: Conservative use of noexcept in the Library

標準ライブラリの関数を見境無くnoexcept指定することへの警鐘。

C++ Freestanding and Conditionally Supported

<thread>をfreestanding headerにするには無理がありすぎるので、廃止。また、コンパイル時にスレッドをサポートしているかどうかを判定できるようにするプリプロセッサーマクロ、__STDCPP_THREADS__を追加。もし、環境が複数のスレッドをサポートしていれば、このマクロが定義される。

また、この決定にともなって、<ratio>と<chrono>もfreestanding headerではなくなる。

n3281: 692. Partial ordering of variadic class template partial specializations

このペーパーの以前のバージョンでは、variadic class templateのpartial orderingでは、関数のすべての仮引数が、partial orderingで考慮されることになっていた。しかし、そもそもpartial orderingを、明示的な実引数がないのに行うのは不自然である。そのため、明示的な実引数がある場合のみ行うように変更する。

これは、当然だと思う。2010年の3月の時点で、私はおかしいと思っていたのだ。本の虫: Template Parameter Packがゼロ個の場合でもpartial orderingで考慮されるかで疑問に思っていたことだ。

これは、かなり大きな変更だ。この変更により、本の虫: テンプレートパラメーターパックがゼロ個でも、Partial Orderingで考慮されるは、現状と一致しなくなる。これは大きな変更だ。本当に大きな変更だ。

template < typename T >
void f( T ) ; // #1
template < typename T, typename ... Types >
void f( T, Types ... args ) ;

int main()
{
    f( 0 ) ; // エラー、曖昧、FDIS以前では#1が選ばれた
} 

穴を塞いだJason Merrillには、感謝すべきなのか。exploitできなくなったので恨むべきなのか。

ただ、私がなんとなく感じていた、ゼロ個のtemplate parameter packにpartial orderingが適用されるということへの違和感は、正しかったことになる。してみれば、私も智慧も捨てたものではない。

n3282: Resolution for core issues 1207 and 1017

core issue 1207は、const修飾されたメンバー関数のtrailing-return-typeでのthisは、constではないという問題への修正である。以下の様なコードが問題になる。

struct X
{
    iterator f() { }
    const_iterator f() const { }
} ;

struct Y
{
    X x ;
    auto f() const -> decltype(x.f()) { return x.f() ; }
} ;

Y::fはconst修飾子がある。。そのため、thisはconst修飾されている。つまり、Y::fの関数の本体における式、x.f()の型は、const_iteratorである。ところが、trailing-return-typeは、影響を受けない。したがって、戻り値の型は、iteratorになってしまう。これはまずい。そのため、メンバー関数におけるthisの型は、const修飾子を考慮する。

core issue 1017は、これに比べると簡単な問題だ。以下の様なコードを許可する変更である。

struct X
{
    int member ;
} ;

struct Y
{
    decltype( X::member ) x ; // OK
} ;

これまでは、定義に従うと、クラスYにおけるX::memberは、(*this).X::memberという風に変換されてしまう。ここでいうthisとは、もちろんYのthisである。これは、当然エラーになってしまう。そのため、未評価式の中では、この変換を行わないように変更する。

n3283: Dependent Bases and the Current Instantiation: Wording for Core Issue 1043

これは、core issue 1043への対応である。

クラステンプレートにおけるname lookupでは、non-dependent nameのみを探す名前探索と、dependent nameもさがす名前探索がある。このTwo Phase Lookupは有名だ。ところで、以下の場合はどうなるのだろうか。

template<typename T> struct B {};
  struct C { typedef int type; };

  template<typename T>
  struct A : B<T>, C {
    template<typename U> type a(); // #1
    template<typename U> typename A<T>::type a(); // #2: different from #1?
  };

  template<typename T> template<typename U> typename A<T>::type
    A<T>::a() { ... } // defines #1 or #2?

unqualified-idであるtypeにたいしては、C::typeが選ばれる。では、qualified-idの場合はどうなるのか。#2は#1とは違う関数なのだろうか。それとも、同じ関数だから、エラーになるのだろうか。ましてや、その関数をクラス本体の外で定義する場合はどうなるのか。

これに対しては、non-dependent base memberが見つかるようにするとの合意が得られた。つまり、現行のgccは間違っている。EDGは正しい。

2011-04-13

std::nullptr_tの型はポインターではない。

以下のコードを考えてもらいたい。果たして、値はtrueかfalseかどちらだろうか。

std::is_pointer<decltype(nullptr)>::value ;

これはfalseである。なぜならば、nullptrの型、decltype(nullptr)のtypedef名であるstd::nullptr_tは、ポインター型ではないからだ。もちろん、メンバーへのポインター型でもない。std::nullptr_tは、nullポインターに変換可能なprvalueの定数という扱いになっている。

いまだにどのコンパイラーも実装してない6年前に追加されたC++0xの新機能

以下のコードは、well-formedなC++0xのコードである。

template < void * = nullptr >
void f() { }

int main()
{
    f() ; // OK
    f<nullptr>() ; // OK
}

まず、function template parameterは、default argumentを取ることができるようになった。これは、gccがサポートしている。

また、non-type tepmlate argumentは、nullポインターに変換可能な定数式を取ることができるようになった(14.3.2 [temp.arg.nontype])。これは、どのコンパイラーも未だに実装していない。したがって、上記のコードは、現行のコンパイラーではコンパイルできない。

はたして、これは単に未実装なのか。しかし、関数テンプレートのデフォルト実引数は実装しているのに、これを実装していないというのは、忘れているんじゃないだろうか。gccにバグレポートすべきなんだろうか。

C++0xにおけるenable_ifの新しい使い方

現在、BoostのML上で、C++0x上における、興味深いenable_ifの使い方が示されている。簡単にいうと、こうなる。

// Never defined
extern void * enabler ;

template < typename T,
    typename std::enable_if< std::is_arithmetic<T>::value >::type *& = enabler >
void f( T )
{ std::cout << "T is arithmetic" << std::endl ;}

template < typename T,
    typename std::enable_if< std::is_pointer<T>::value >::type *& = enabler >
void f( T )
{ std::cout << "T is pointer" << std::endl ; }

int main()
{
    int arithmetic = 0 ;
    f( arithmetic ) ; // T is arithmetic
    int * pointer = nullptr ;
    f( pointer ) ; // T is pointer
}

このように、enable_ifをNon-type template parameterとして使い、enablerというstaticストレージ上の変数をデフォルト引数に渡してやるのだ。このenablerは、定義する必要はない。これによって、仮引数や戻り値の型といった、どうも使いたくない部分にenable_ifを使わずにすむ。また、以前はenable_ifが使えなかった場合でも、使えるようになる。

しかし、なぜenablerが必要なのか。以下の形ではだめなのか?

typename Reserved = typename std::enable_if<条件式>::type >::type

実は、この形では、オーバーロード出来ないという問題がある。実際に試すと分かりやすい。

template < typename T,
    typename Reserved = typename std::enable_if< std::is_arithmetic<T>::value >::type >
void f( T ) { }

template < typename T,
    typename Reserved = typename std::enable_if< std::is_pointer<T>::value >::type >
void f( T ) { } // エラー、再定義

この二つの関数は、どちらも同じ宣言として扱われる。そのため、オーバーロードはできない。

オーバーロードがふたつだけならば、ゼロ個のテンプレートパラメーターパックを使うという手もあるが、三つ以上のオーバーロード関数があれば、その手は使えない。このenablerを使うテクニックならば、オーバーロードがいくつになっても対応できる。

しかし、なぜvoidへのポインターへのリファレンスなのか。以下の形ではだめなのか。

std::enable_if<条件, int>::type = 0 

これは動くが、ユーザーが誤ってテンプレート実引数を明示的に渡してしまうかもしれないというおそれがある。その点、void &&という型は、まず現実に使われないので、安全である。およそ、void *&が一体どういう型であるかを理解できるようなユーザーであれば、このenable_ifのテクニックも理解できるほどのC++上級者なので、やはり間違えることはない。

さて、このテクニックを応用することで、コンストラクターや型変換関数にも、enable_ifを使うことができる。

// Never defined
extern void * enabler ;

class X
{
public :
    // 引数を10個だけ受け付けるコンストラクター
    template < typename ... Types,
        typename std::enable_if< sizeof...(Types) == 10 >::type *& = enabler >
    X( Types ... args ) { }

    // 数値型へのみ、型変換をする変換関数
    template < typename T,
        typename std::enable_if< std::is_arithmetic<T>::value >::type *& = enabler >    
    operator T() { return static_cast<T>(0) ; }
} ;

int main()
{
    // OK
    X x1( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ) ; 
    // エラー、引数が10個ではない
    X x2( 1, 2, 3 ) ;

    int i = x1 ; // OK
    double d = x1 ; // OK

    int * ptr = x1 ; // エラー、数値型ではない
}

現在、このテクニックをBoostで公式にサポートするべきか、議論が行われている。問題は、boost::enablerという識別子を導入するかどうかだ。やはり、

typename boost::enable_if_c< ... >type *& = boost::enabler

という形は、どうも見慣れない。もちろん、およそenable_ifを使うほどの人間は、この意味を理解できるほどのC++上級者であろうから、問題はないのだが、やはり公式にサポートするべきかどうかは議論が分かれる。

このイディオムをサポートするならば、そのための特別なメタ関数、enable_whenを提供するべきだという意見もある。これは、単にnested typeがvoid *&になるだけのものだ。

しかし、Boostのドキュメントでこのテクニックを紹介することには、みな同意しているようだ。公式にenablerを導入するかどうかはともかく、このテクニック自体は、近々ドキュメントに載ると思われる。

2011-04-12

Perfect Fowardingとは何か

Perfect Fowardingとは、関数テンプレートの引数としてのリファレンスを、別の関数に渡す際のテクニックである。ふたつの問題がある。

名前のついたrvalueリファレンスの変数はlvalueである。

int && r1 = 0 ;
int && r2 = r1 ; // エラー、r1はlvalue

これは当然である。なぜならば、rvalueリファレンスは、rvalueを束縛して、lvalueのように使うリファレンスである。リファレンスが勝手に、rvalueとして認識されてしまっては困る。もし、rvalueとして使いたい場合は、明示的なキャストが必要である。

int && r1 = 0 ;
int && r2 = std::move( r1 ) ; // OK

また、以下の様な形の関数テンプレートを宣言するとき、

template < typename T >
void f( T && ref ) ;

refはlvalueリファレンスにもrvalueリファレンスにもインスタンス化される。

template < typename T >
void f( T && ref )
{ }

int main()
{
    int l = 0 ; // lvalue
    int && r = 0 ; // rvalue

    f( l ) ; // Tはint &型、&&は無視される、refの型はint &型
    f( r ) ; // Tはint型、refの型はint &&型
}

これは一見奇妙だが、そういうルールになっている。このルールによって、ひとつのテンプレートで、lvalueとrvalueの両方に対応できる。

しかし問題は、この関数fから、さらにrvalue referenceを他の関数に渡したい場合だ。

void g( int & ) ; // #1 void g( int && ) ; // #2 template < typename T > void f( T && ref ) { g( ref ) ; // 常に#1を呼ぶ }

このコードは、rvalueを正しくforwardしない。常に#1を呼ぶ。なぜならば、たとえrefの型がrvalueリファレンスだったとしても、ref自体はlvalueだからだ。

何故こうなっているのかというと、関数fは、関数gを呼び出した後に、refを使うかもしれない。その際、refがrvalueとして関数gに渡されていては、ムーブされてしまうかもしれない。この意図せぬムーブを防ぐために、rvalueリファレンスはlvalueなのだ。

では、rvalueとして渡したい場合は、std::moveを使えばいいのか。実は、それでは問題がある。関数にlvalueが渡されたときにも、ムーブしてしまうからだ。

template < typename T >
void f( T && ref )
{
    g( std::move(ref) ) ;
}

class X { /* 実装 */ } ;
int main()
{
    X x ;
    f( x ) ; // lvalueとして渡す
}

ここで必要なのは、refがlvalueリファレンスの場合はlvalueで、rvalueリファレンスの場合はrvalueで、関数gに渡すことである。このために、std::forwardがある。

template < typename T >
void f( T && ref )
{
    g( std::forward<T>(ref) ) ; // 常に#1を呼ぶ
}

このように書くだけで、std::forwardは、lvalueのときにはlvalueで、rvalueのときにはrvalueとして、魔法のようにパーフェクトなforwardingをしてくれる。

2011-04-09

読書感想

生物と無生物のあいだ (講談社現代新書)

動的平衡 生命はなぜそこに宿るのか

最近、福岡伸一の本を読んだ。分子生物学の観点から、生命というものを説明している本だ。感想としては、あまり文章がよろしくない。また、話題がかなりバラバラで、繋がりが見られない。問題を分かりやすく説明しようとして使っている比喩も、かえって問題を分かりにくくしているように感じる。ただし、内容は面白い。

分子生物学に関する様々な話を、どうもいまいち各話の繋がりが薄いように思われるが、非常に興味深く書いている。

生命とは、動的平衡である。その複雑な仕組みや、一部の遺伝子がノックアウトされても、回避を試みるなどの性質を考えれば、遺伝子組換えというのは、そんなに簡単に問題を解決しないと論じている。

ところで、作中で紹介している。シュレディンガーの"What Is Life?"を読もうとしたが、どうも英語が難しい。おいおい読むことにしよう。

天皇たちの孤独―玉座から見た王朝時代 (角川選書)

少し前から借りたままで、どうも読み進められなかった本。今朝、少し早く目がさめたのを幸いに、一気に読むことにした。

繁田信一による、王朝時代の孤独な天皇達の実情を書いた本。一条天皇、円融法皇、東三条院、花山法皇、上東門院、三条天皇について書いている。王朝時代とは名ばかりで、その実は摂関政治である。その時代の天皇は、孤独だったという。

あとがきで、「桐壺帝は正しかった」と述べているのも、納得できる内容だ。源氏物語で、光源氏は天皇になりそこなった人間という認識があるが、桐壺帝は光源氏を愛していたからこそ、天皇にならなくてもよい道を提供したのではないかということだ。そういう認識では、光源氏は天皇より恵まれていたのである。しかし、それでも作中で光源氏も色々と苦悩している。それでも悩みはつきないものなのだろう。

最近のPCゲームに関する考察

Fallout 3以降、ゲームらしきゲームをしていない。カネがないというのもあるが、私の目には、どれもありきたりの凡作に見えるのだ。近年、YouTubeを初めとした動画サイトの興隆によって、実際のプレイ動画によって、クソゲーがすぐに確かめられるので、いい時代になったと言える。

なぜパブリッシャーは、実際のプレイ動画をもっと公開しないのか。Trailerは大抵、実際のプレイ動画ではなく、3DCGソフトによって描画されたムービーなのだ。実際のゲームによる描画ですらないとは、恥ずかしいと思わないのだろうか。

もっとも、実際のプレイ動画を公開してしまうと、メッキがはがれてしまうからなのかもしれない。まったくもって、動画サイトの進歩に感謝するばかりだ。

とはいえ、ここ数年ゲームから離れていると、ゲーム界隈の技術からも離れてしまう。もちろん、私が知るのは、単なる表面上の知識だけなのだが、それでも、一応、最近のPCゲーム事情を、久し振りに長々と考察してみることにする。ただし、私は実際にプレイしておらず、他人のプレイ動画の観察によってこの考察を書いているので、的が外れているかもしれない。

プラットフォームについて。

まず、ゲームはマルチプラットフォームが当たり前になったということだ。主にWindows向けのPC、XBox360、PS3、これらのプラットフォームに、同時に同じゲームを販売するということは、もはや当然である。

このマルチプラットフォームは、PCゲームに、いくらか影響を及ぼしている。まず、従来ならばコンソールでしか発売されなかったであろうゲームを、PCからも遊べるということだ。これは利点だろう。ただ、欠点もいろいろと目につくのである。

XBox360とPS3のハードウェアの性能は、現在のPCからみれば、化石である。博物館に陳列されていてもおかしくないほどの、考古学的価値のある化石である。そもそもコンソールのスペックは、登場時点ですでにPCより劣っていたのだから、当然なのだが、さすがに2011年ともなると、この違いは悲惨である。

現行のPCのハードウェア性能ならば可能である表現が、コンソールの存在によって、妨げられてしまうのである。手間を考えると、プラットフォームごとの差異はなるべく少なくしたい。PC向けに特別なコストをかけたくない。となると、表現は、全プラットフォーム向けで実現可能でなければならない。劣ったコンソールにつられて、PCでの表現も制限を受ける。

ただ、これには、あまり誇らしくない利点もある。未来のハードウェアを要求するゲームが販売されなくなったのである。これまで、PCゲームは、発売当初は非現実的に動作が重いというのが珍しくなかった。将来のハードウェアの進化をみこんで、現在のハードでは手に余る処理を行ったり、また、単に手間をかけたくないために、それほど速度を考えずに実装したりしていたためである。

今や、そのような馬鹿げたゲームは発売できなくなった。コンソールの処理速度は固定だからだ。もしコンソール所有者がゲームを買って、ハード性能が足りないがための処理落ちを経験したならば、購入者はそれを、不具合だと考える。当然である。性能は固定なのだから、そのプラットフォーム向けに販売する以上、そのプラットフォームで動くべきなのである。

コンソールの化石スペックとあいまって、無謀に重いPCゲームは、発売されなくなった。これは、利点である。思うに、プログラマーには、制限が必要なのではないかと思う。制限がないと、プログラマーという人種は極限を求めてしまうため、現行ハードでは到底実行不可能な重いプログラムができあがってしまうのだ。

もうひとつの欠点とは、UIの問題である。コンソールは、マウスとキーボードで操作しない。ゲームパッドで操作する。一方、PCでは、ゲームパッドも使用可能である。コンソールを考慮すると、ゲームパッドでの入力を前提にしたUIになってしまう。そして、これが非常によろしくない。

ましてや、ゲームパッドでFPSというのが理解に苦しむ。現行世代のコンソールが登場して、FPSゲームも販売されると聞いたとき、私は信じられなかったのだ。まさかゲームパッドで操作するとは信じられなかったのだ。驚くべきことに、ゲームパッドでFPSを操作するのである。私は未だに信じられない。

いわゆるFirst Person Shooterというジャンルのゲームでは、画面表示は一人称視点、あるいはそれに準ずる三人称視点であり、視点の方向はマウスで動かすものである。ゲームパッドでこれを実現しようとなると、必然的に、親指アナログスティックで視点を操作することになる。聞けば、最近のコンソールゲームしか遊ばない若者は、マウスとキーボードでのFPS操作ができなくなっているそうではないか。信じられない。あるいは、私も歳を取ったのだろうか。

もちろん、スティックはマウスほど正確な操作ができないということは、ゲーム開発者も承知していて、それなりの処置が取られている。たとえば、敵の方向に視点をロックするだとか、撃った弾は魔法のように敵の方向に向かってホーミングするだとかの類である。

これ自体は、別に悪いとは思わない。もうハイパーオリンピックやスーパーマリオブラザーズの時代ではない。連打速度とか、極めて正確なミリ単位の操作が求められる時代ではない。ただ、そうまでしても、やはりスティックが正確な操作には向いていない。結果として、aiming感は最悪である。

唯一の利点は、有線のXBox360 Game Controllerは、PC用のゲームパッドとして最適であるということぐらいだろうか。このゲームパッド自体、なかなか悪くない出来だ。エレコムやサンワの安かろう悪かろうなゲームパッドとは雲泥の差である。また、マルチプラットフォームのゲームのPC版は、XBox360のゲームパッドを前提にした設計になっており、コンソールと同じ感覚で遊べるというのも悪くない。レーシングゲームならパッドのほうがいいに決まっている。ただし、やはりFPSは無理だ。

実際に販売されたゲームから考えて、やはりコンソールは害悪であると思う。かつてPCゲームとして面白かったゲームが、マルチプラットフォームになったばかりに、非常に制限されたものになってしまっているのだ。ほとんどのゲームが、コンソールの影響を受けているので、いちいちゲームタイトルを挙げるにも及ばない。

グラフィックについて。

グラフィックは非常に進化した。コンソールの性能という足かせのせいで、ポリゴン数やテクスチャーは変わらないが、代わりに、シェーダーによる見せ方が非常に向上した。特に、Defferred Shadingは素晴らしい。

思うに、ポリゴン数やテクスチャー自体の底上げが見られないのは、いまだにDVD二層の容量である8GBが足かせとなっているためだろう。また、リソースのサイズが増えると、それだけロード時間もかかってしまうので、仕方がないのだろう。光学ディスクとHDDが廃れるまでは、この状況は変わらないだろう。

それに、ポリゴン数やテクスチャーの質が明らかに分かるのは、極端に拡大された場合であり、大部分はそれほど拡大しないゲームにおいては、リソースの質の底上げはあまり意味を成さないだろう。むしろ、ポリゴンによる描画以外の手法を研究すべきだろう。たとえば、ボクセルとか。

ゲームの表現について。

ゲームの表現方法は、かなり進歩が見られる。思うに、ゲームはどんどん親切になっている。たとえばチュートリアルだ。FPSにありがちな、「しゃがむ」という動作のあるゲームでは、しゃがまなければ進めないような道を最初に用意する。「ジャンプ」という動作のあるゲームでは、ジャンプしなければ通れないような道を最初に用意する。これらのはからいによって、プレイヤーはゲームを進めつつ操作方法を覚えることができる。

また、途中で詰まらないように、攻略のヒントを出してくれる神のような存在がいることである。「ゼルダの伝説 時のオカリナ」におけるナビィのような存在がいる。もちろん、実際に見えているわけではなく、単なる通信という形をとった声だったりする。また時には、もっと露骨に、ゲーム中の空間に文字やアイコンが浮かんでいたりするし、常にコンパスが目的地を指している場合もある。「ゼルダの伝説」のように、ノーヒントで何もない壁を爆破して入口を見つけたり、草を燃やして階段を見つけたりといった、ありとあらゆる動作を総当りさせるような、単純な謎解きは、もはや時代遅れである。

また、ロード時間をごまかすための工夫も見られる。ロード時間はストレスがたまるものである。ロードが必要なところでは、カットシーンやムービーを流したりなどをすることもある。また、次の場所までの間に、やたらに細長い道を作って時間稼ぎをしたり、また最小限のインタラクティブ性をいれるなどすることもある。たとえば、ドアを開けるのにボタン操作を必要としたり、一歩足を動かすために、タイミングよくボタンを押さなければならないなどの操作である。どちらも魂胆が露骨に見え透いているのだが、少なくとも、ロード画面で待ちぼうけを食らわすよりはマシだろう。

ゲームの面白さについて。

最近のゲームは、ますます大規模になっている。開発費用もうなぎのぼりだ。しかし、どうもゲームはあまり面白くなっていないと思うのは、私だけだろうか。特に、多額の開発費用をかけたゲームほど、つまらなくなっているような気がするのだ。

たしかに、グラフィックはいいのだが、それだけでは良いゲームたりえない。グラフィックだけはいいものの、細かい操作性に、いちいちストレスがたまるゲームがいかに多いことか。

結局、現行世代のコンソールに制限を受けている限り、ゲームは進化しない。次世代コンソールがでるまで、しばらくゲームは不作が続くだろう。

2011-04-01

Skyrimのインタビュー

The Elder Scrolls Evolved: What's New in Skyrim - PC Feature at IGN

興味深い情報だけを要約。とくに、HavokとDirectX 11だろう。

Havok Behaviourは、アニメーションを動的にリアリスティックに表現するための技術である。例えば、下り坂を走る場合を考えてみる。アニメーションが静的だと、平面を走るときのアニメーションを使うので、非常に不自然に見える。Havok behaviourは、これを動的に修正してくれる。インタビューでは、物理演算には使っていないといっている。これは希望が持てる。Hovakの物理演算はひどかった。

DirectX 11は、DirectX 10やDirectX 9世代のGPUでも使うことができるそうだ。もちろん、新機能は使うことができないが、DirectX 11は、提供されている機能を、以前のようなCaps地獄に陥ることなく選択的に使えるよう設計されているそうだ。APIとしてのDirectX 11は使っているが、新機能(テッセレーションなど)は使っていないという意味だろう。

Havokを使っているが、物理演算には使っていない。Havok Behaviorをアニメーションに使っている。

Fast Travelはある。すでに行った場所にはFast Travelできる。さらに別の移動システムがあり、まだ行ったことのない場所に行くことができる(馬車をレンタルするようなものか?)

エンチャントは存在する(先の情報では、まだ未定ということであったが、明言された)

スキルは、使うと上がる。Falloutのように、レベルアップで割り振るのではない。

最も要望された要素がふたつある。ドラゴンとマルチプレイヤーである。我々は今回、ドラゴンを実装した。マルチプレイヤーについては、色々と構想はあるが、今回も実現していない。

問:2011年11月11日にリリースできると確信している。

DirectX 11はサポートしている。しかし、質問の本質は、DirectX 11の新機能を使っているかどうかだろう。答えは、「いや、そんなに」だ。

問:馬鹿な質問ですがユニコーンはでてきますか?

答:そりゃDLCだな。100ドルだよ。(ジョーク)

問:ドラゴンに乗れますか?

答:自分の意志で乗るとはいえない。

問:レビテーションは?

答:それもDLCだな。300ドルだ(ジョーク)