2011-01-31

玄米の炊き方

玄米を鍋で炊くにあたって、気をつけることがふたつある。白米と同じ感覚では炊けない。

水を多くしても、あまり意味がない。

玄米はあまり水を吸わない。そのため、余分な水は、水として残る。重湯にもならない。玄米のおかゆは、普通の鍋ではつくりにくい。圧力鍋があれば作れるのかもしれない。

加熱時間を長くする。

白米より加熱時間がかかる。

白米と同じ時間では、短すぎる。おそらく、玄米は焦げにくいと思うので、5分か10分ほど長く火にかけても構わないだろう。

今日は玄米食をあきらめた

さて、この二日間、玄米を食べてきたのだが、今日は、純粋な玄米食をあきらめてしまった。米を炊こうと玄米を取り出したのはいいが、また今日も玄米を食べるのかと思うと、ものすごく嫌な気分になったのだ。なにしろ、米のように水をたっぷり吸ってくれないし、たくさんかまなければならないので、顎が疲れる。今日も玄米を食べなければならないとは悲惨だ。

結局、白米に25%ほど玄米を混ぜて炊くことにした。これなら美味しく食べられる。

なるほど、書物に書かれている、当時の日本人は玄米を嫌ったということは、正しかったのだな。純粋に玄米だけを食べるのは、かなり苦痛である。やはり、何事も実践してみなくては分からない。

Google Transliteration

Type in Japanese - Google Transliteration
ブラウザの中でも日本語入力 -- Google Transliteration サービス日本語対応 - Google Japan Developer Relations Blog

ブラウザーだけで日本語入力ができる。ブラウザ上にしてはUIも、通常のIMEと遜色ない。

2011-01-30

玄米の炊飯に成功した

二度目の挑戦で、玄米の炊飯に成功した。精白米と違い、なかなか難しい。

白米を炊くのには、あまり加熱する必要はない。例えば、加熱は沸騰するまでであったとしても、余熱で食べられる状態になるだろう。そういう意味では、白米の調理は、玄米より燃料が少なくて済む。

ところが、玄米はそうはいかない。玄米は、ある程度の時間、高温を保たなければ調理できない。前回の失敗は、水が多かったのと、加熱時間が少なかったからである。

玄米は、あまり水を吸わない。白米ならば、水を多少多めにしても、炊きあがりはあまり変わらない。ただ、米が水を多く吸うだけである。ところが玄米は水をあまり吸わない。多すぎた水は、単に水として残る。だから、玄米のおかゆというのは、白米のおかゆとはだいぶ異なる。

食感と味についてだが、これは白米とはぜんぜん違う。玄米を白米と同じように炊くと、非常にパサパサしている。しかも固い。よく噛まなければ食べられない。味についても、白米のような優しい味ではない。

ただ、まずいというわけでもない。これはこれで美味しい。食べるのにアゴが疲れるという問題点を無視すれば、毎日玄米でも、それほど悪くないかもしれない。

もっとも、今は物珍しいから、味は悪くないと思っているだけなのかもしれない。とりあえず、今回買った1kgの玄米を食べ終えてから、玄米に切り替えられるかどうか考えよう。

白米と混ぜて炊くという手もある。

2011-01-29

玄米を買ってみた

玄米である。かつて日本人は、精米を食べていたために、多くが脚気にかかったと、文献には書かれている。また文献は言う。当時陸軍兵士をもっとも殺したのは森林太郎である。なぜならば、脚気が栄養の欠乏によることを認めず、玄米食を阻止し続けたからであると。また、これには、兵士は白米を食べられるという期待もあり、容易に玄米に変えることができなかったからだとする文献もある。

また文献は言う。玄米はまずいと。曰く、玄米は固く、臭いが悪く、食味にも劣り云々。

ところで、最近の健康志向(笑)とやらは、盛んに玄米を栄養に優れているともてはやす。

栄養に優れているとはいえ、今日では肉や卵が安価で手に入るので、わざわざ玄米にこだわる必要はない。日本のような豊かな国では、より多くの品目から栄養をとったほうが良い。遺伝子組み換えによって栄養価を増やしたゴールデンライスは、肉や卵はおろか、他の野菜すら容易に手に入らない、発展途上国のために開発されたのだ(もっとも偽善者は、遺伝子組換えというだけで反対して、発展途上国の子供を、結果的に殺しているわけだが)

しかし、玄米の味の記述については気になる。果たして、玄米はそんなにまずいものなのか。これは自分で試してみるべきだ。

というわけで、いつも米を買っている近所の米屋にいって、玄米を買ってきた。この米屋は、まだ専売制度が残っていた頃からの、昔ながらの米屋だ。とくに店頭販売というほどの小奇麗な店舗でもなくて、精米のついでに、米を売っているような店だ。ただそれでも、売上の半分は、ホテルなどに卸すのではなく、店頭販売によるものだというから、不思議なものだ。

とりあえず、玄米だけで炊く準備をした。

追記:とりあえず2時間ほど給水させて炊いたところ、おかゆになってしまった。水が多すぎたらしい。

どの魔法のアイテムが欲しいか選びなさい

redditで面白かった話題。

Which Of These Ten Magical Items Would You Choose? And Why? : AskReddit

どの魔法のアイテムが欲しいか選びなさい。また、それは何故か答えなさい。

  1. 一日一回、1000キログラムのいかなる食物でも生成できるポット
  2. 自分の周囲半径250kmが完璧な天候に保たれる腕輪
  3. 本に触っただけで一瞬にして知識を吸収できる首飾り
  4. 異性(同性愛の場合同姓)にもてまくるが、自分が好きな人には効果がない、無限に湧く香水
  5. 一日一回、一分以内ならば時間を戻せる時計
  6. 鳴らすと、生物以外の物ひとつを、一分間で直せる鈴
  7. 一口食べると、160歳まで無敵で若々しさを保てるチョコレートバー、12本
  8. 燃料いらず、メンテ不要、8人乗りで、地球上のどこにでも1秒で行ける車
  9. 自分をどんな他人にでも、自由に変身でき、自分の意志でいつでも解除可能なリモコン
  10. 毎回、うんちをするたびに、金貨を一枚ひり出す不死の犬

利用方法はいくらでも思いつく。

1.は素晴らしいポットである。単にどんな場所でも飢え死にしないというだけでなく。大金を得ることも可能だ。たとえば、1000kgのキャビアを出したら、それだけで大儲けできる。また、世界一うまい料理を1000kg出せば、世界一のレストランを経営することも可能だ。いや、キャビアどころではない。定義からすれば、どんな「食物」でも出せるのだ。純金入りのワインがある以上、金も食物に分類されるはずだ。いや、キャビアや金などを無価値にできる、もっとすごい活用法がある。たとえば、ティラノサウルスの卵とか、マンモスの肉、始祖鳥の焼鳥なども生成可能ではあるはずだ。ジュラシック・パークが実現できるではないか。

2.も、「天候」の定義次第で、すばらしいアイテムである。雨が降ってほしい地域に雨が降るのはもちろんの事、核の冬だとか、隕石だとか、そういうことも、自分の半径250km以内には起きないわけだ。

3.も素晴らしい。本屋や図書館を巡って、本をひとなでしてくるだけで、世界一の天才になれる。

4.の価値は、果たして良いものか悪いものか判断しがたい。簡単にいえば、マイケル・ジャクソンになれるわけだ。香水は無限にあるのだから、たとえば、浄水場に香水をぶちこんだら、だいぶカオスなことになりそうだ。

5.は、一分以内ならば、どんなことでも自由にできるわけだ。一分以内に起こった、あらゆる出来事は、なかったことにできる。一分以内に行える犯罪ならば、捕まる心配はない。ただし、結果を引き継ぐことができないのだが。

6.を使えば、小金を稼ぐことができる。たとえば、廃車となったフェラーリやランボルギーニを一分で直すことができる。壊れた古美術品や、保存状態の悪い写本の類も、直すことができる。ただし、ベルはひとつしかないのだから、商売の手を広げることはできない。他には、一分以内に壊れない原子力で動く兵器をつくって、世界を征服することもできるかもしれない。

7. 自分で一口かじるのはもちろんだが、残りをどうするかという問題がある。気の合う友人達に与えて、160歳まで楽しく暮らすのか。それとも、全部自分で食べて、数千年生きるのか。

8. 原子力発電所やペンタゴンの中にも、自由に入れる素晴らしい車だ。

9.は、単純に素晴らしいリモコンである。どんな犯罪も露見しないであろう。仮にバレても、一生他人のままで過ごせばいい。

10.は、他のアイテムに比べると見劣りするものの、金の価値は安定しているので、それなりの生活が送れるだろう。犬も可愛い。

もちろん、落とし穴があるかもしれない。これらの魔法のアイテムを、実際に使っている場面を、他人に見られたらどうなるか。盗まれるか、殺されて奪われるか、ハッピーエンドとはならないであろう。ましてや、どこにでも行ける車など、セキュリティもなにもあったものではない。国家政府が、そのようなアイテムを所有している人間を見逃すはずがない。政治犯として世界的に犯罪者扱いされてしまうだろう。アイテムの使用には、十分注意しなければならない。

1. キャビアや金に価値があるのは、少量しか手に入らないからである。一日1000kgも、無から生み出せるような装置があれば、価値はやや減じてしまうだろう。そのような価値の低下は、政治的にも好ましくない。常に暗殺に気をつけなければならない。

3. 知識を吸収とは、一体何を意味するのか。たとえば、必要なときに情報を引き出せるだけならば、インターネットにつながったラップトップと、あまり大差はないだろう。知識と創造性は別である。たとえ、あらかじめ本に書かれた知識しか得られないのであれば、創造性にはあまり役に立たない。インターネットの発展によって、既存の知識を得るコストが極端に下がった現代でも、人間の創造性は、それほど向上していないのである。

4. 果たして、マイケル・ジャクソンは幸せだったのだろうか。

5. 時間を1分間戻せるというのは興味深い。しかし、あらゆる出来事が1分前に戻ってしまうのは、果たして役に立つのだろうか。たとえば、金品を盗んだとしても、時間を戻してしまえば、自分の手元には存在しないわけだ。そもそも、自分の身体自体も、出来事の影響を受ける。もし、自分の身に起きた結果も戻すとなれば、1分間先の記憶はないはずだ。記憶がない以上、単に実験して楽しむということすらできない。たとえば、むしゃくしゃしたからテレビを壊すとする。一分以内に時間を戻せば、テレビは実際には壊れない。しかし、あらゆる出来事はなかったことになるのだから、テレビを壊したという記憶すらないはずだ。それは、起こっていないのだ。もちろん、過去の自分にメモを残すこともできない。自分の身体は、結果を過去に持っていけるとしよう。その場合、だいぶマシにはなる。ただし、自分が銃で撃たれて、助かるために時間を戻したとしても、やはり怪我は治らないことになる。このアイテムには、自分の記憶だけは過去に持っていけるという、都合のいい機能が必要である。

10. 「金貨一枚とは実に少ない。ひょっとしたら、犬の体内には大量に金貨が詰まっているのではないだろうか」、この発想をしてはいけない。

2011-01-26

すごいクオリティの動画

Falloutのファンメイド動画。

これはすごい。相当の手間暇がかかっているはずだ。

Fallout: Nuka Break

odr-usedは参考書では使わないことにした

規格の文面で、単なる一般動詞である「使う(use)」と、ODRの文面での「使う(use)」の使用が紛らわしいということで、新たに「ODR使用(odr-used)」という用語が導入された。

ODRが重要になる文面では、例えば、「もし、関数が使われている場合・・・」ではなく、「もし、関数がODR使用されている場合・・・」などとなる。

規格を厳格に考えるならば、当然、参考書でもODR使用という用語を使うべきである。考えたが、ODR使用という用語を参考書で使うのはやめておくことにした。もとより今執筆している参考書は、規格の翻訳ではないし、そこまでの厳密さを求めるのならば、そもそも規格を読むべきなのだ。

2011-01-25

C++03とC++0xにおける一時オブジェクトの寿命の違い

今書いているC++0xの参考書では、一時オブジェクトの寿命に関しても、当然、規格の範囲内で、詳細に解説する。

一時オブジェクトの寿命は、一時オブジェクトが構築された場所からみてのfull-expressionを評価した後である。ただし、これには例外がふたつある。興味深いことに、C++03とC++0xで、例外が違っているのだ。

一時オブジェクトをリファレンスで束縛すると、一時オブジェクトの寿命が、リファレンスの寿命に延長される(ただし例外あり)というのは同じだ。違うのは、もうひとつの方だ。

C++03では、オブジェクトを定義する宣言子の初期化子によって構築された一時オブジェクトの寿命は、そのオブジェクトの初期化終了までとされている。

struct X { } ;

int main()
{
    X x001 = X(), x002 = X(), x003 = X(). ;
}

例えば、上のようなコードがあり、実装がX()という式で、一時オブジェクトを構築するとする。x001のために構築した一時オブジェクトの寿命は、x001の初期化終了までである。

しかし、良く考えてみると、このコードにおけるX()のfull-expressionというのは、initializer内の式である。ということは、これはすでに、一般的なルールの範囲ではないのか。とすると、このような例外的なルールは、必要ないということになる。

C++0xでは、この例外的な定義は消えた。しかし、代わりに、新しいルールが追加された。

C++0xでは、配列の要素を初期化する際に、デフォルトコンストラクターがデフォルト実引数を持っていた場合、ある要素のデフォルトコンストラクター実行における一時オブジェクトの破棄は、次の要素の初期化の「以前にシーケンス(sequenced before)」される。

struct Fat { char [1000] ; } ;

struct X
{
    X( Fat f = Fat() ) { } // デフォルトコンストラクター
} ;

int main()
{
    X a[1000] ;
}

この例では、a[0]からa[999]までの1000個のX型の配列の要素に対し、デフォルトコンストラクターが呼び出される。もし、a[0]がa[1]の前に初期化された場合、a[0]のデフォルトコンストラクター呼び出しによって構築されたFat型の一時オブジェクトは、a[1]を初期化するときには、すでに破棄される。もちろん、あらゆるサイドエフェクトなども、以前にシーケンス(sequenced before)される。

YouTubeが貼りつけコードをついに変えた

今朝、ふと気がついたら、YouTubeの動画埋め込みが、私の環境でも見られるようになっていた。これはつまり、video elementを使っているのだ。これは一体どうしたことだろう。

不思議に思ってYouTubeを見てみたところ、どうやら、ついに新しい動画埋め込みのコードを正式に提供するようになったらしい。以前、それ用にbookmarkletを書いたこともある、あのコードだ。

また一歩、Flashの撲滅に前進したわけだ。

FPSゲームの歴史

PC向けのアクション性の高いFPSゲームの歴史を、実際のプレイ動画だけで振り返る動画をみた。

実のところ、私はアクション指向のゲームは好きではないので、動画で上げられているゲームは、一つとして遊んだことがない。ただ、こうして見てみると、ゲームの進化を感じられる。

こっちは、C64のゲーム集。

Amigaのゲーム集

2011-01-24

これは微妙すぎる

12.1 Constructors [class.ctor] paragraph 14が微妙すぎる。果たしてこれは解説すべきなのだろうか。

1984の現代版

高木浩光@自宅の日記 - このまま進むと訪れる未来 岡崎図書館事件(15)

2011-01-23

unionの暗黙のデフォルトコンストラクタ

こう書いてある。

12.1 Constructors [class.ctor] paragraph 5

A defaulted default constructor for class X is defined as deleted if:
(省略)
any *non-variant* non-static data member of const-qualified type (or array thereof) with no brace-or- equal-initializer does not have a user-provided default constructor,

X is a union and *all* of its variant members are of const-qualified type (or array thereof),

non-variantという条件があるから、一つ目の条件は、variant memberには当てはまらない。つまり、unionには当てはまらない条件である。(おそらく規格外ではあまり使われていないだろうが、unionの共用されるデータメンバーには、規格ではvariant memberという名称がある。)

二つ目の条件には、all of its variant membersとある。とすれば、const-qualified typeとconst-unqualified typeの両方をvariant memberに持つunionのデフォルトコンストラクターは暗黙にdeletedされないはずである。ということは、以下のコードはwell-formedということになる。

union X
{
   int a ;
   int const b ;
} ;

int main ()
{
   X x ; // well-formed
}

しかし、MSVC、GCC、Comeauは、すべて、エラーとする。

もちろん、これらのコンパイラーは、まだC++0xを完全に実装していない。しかし、このような根本的な言語仕様は、C++03から変わっていないのではないだろうか。C++03の規格の定義では、「もし、ユーザーによって書かれた、暗黙のデフォルトコンストラクターと同等のコードがill-formedになる場合は、ill-formedである」などと定義されている。C++0xでは、explicitly-defaulted functionや、deleted definitionがあるので、いつdeleted定義されるのかというルールを詳細に記述している。そのルールは、C++03を受け継いでいるはずだ。このような根本的な仕様がC++0xで変更されたのだろうか。

不思議に思って質問してみると、だいぶ最近の、Core issue 922による変更らしい。2009年の11月だ。

結論として、unionのvariate memberは、constと非constな型を混ぜて使える。その際、ひとつでも非constな型があれば、暗黙のデフォルトコンストラクターが使える。

ところで、C++0xのunionは、コンストラクターやデストラクターも含むメンバー関数を持てるので、だいぶ様変わりしている。C++0xでは、unionはかなり使いやすくなったはずだ。

再会

昨日の夜のこと、突然、小・中学校時代のある同級生から、メールがきた。何でも、このブログを発見したらしい。別の高校に行ったので、もうかれこれ8年か9年は会っていない計算になる。

しかし、何という偶然だろうか。実は同じ京都市に住んでいるという。京都に引っ越してきたのは、私と全く同じタイミングで、今まで同じ市に住んでいながら、お互い知らなかったということになる。

すでに真夜中であったが、とりあえず飲みに行くことにした。久しぶりに会った友人は、だいぶ変わってはいたものの、昔の性格を考えてみれば、特に不思議はない変わり方であった。単に、我々はお互いに年を取ったのだろう。

ひとつ問題が持ち上がった。店についてから気がついたのだが、どうやら道中のどこかにメガネを落としてしまったらしい。これは困った。簡単に見つかる気もしないので、仕方なく、店にもどることにした。まだ酒を一滴も飲んでいないのに、これでは先が思いやられる。

その後、我々は、ひどい店に行き、まったくもって感心できない飲み方をし、痛く酔った。契闊談讌、心念旧恩まではよかったのだが、杜康の度が過ぎた。それにしても、ひどい店だった。あれは通報すれば営業停止になるレベルだ。

帰宅したが、やはりメガネはない。ひと眠りして、正午に交番に行き遺失物の届けをだした。その足で木屋町に向かい、ひと通り歩いてみたが、やはり、メガネは落ちていない。おそらく、行きに乗ったタクシーの中ではないかと思う。参ったことに、タクシーの系列には注意を払わなかったのだ。管理された大規模なタクシーの系列が絞り込めれば、見つかる可能性は高くなると思うのだが。

タクシーの運転手が真面目であることを願う。

しかし、眼鏡がないのは、実に困る。昔のメガネはあるが、これは少し度が合わなくなっている。来週見つからなければ、おそらく出てくることはないだろう。

2011-01-22

union-like classとvariant memberは解説しなくてもいいだろう

union-like classとvariant memberという用語は、クラスの特別なメンバーの一部が、暗黙に生成されるかどうかというところでしか使われない。たったの数カ所でしか使われていないのだ。

もし、Xがunion-like classで、non-static variant memberがユーザー定義のコンストラクターを持つ場合(union-like classとvariant memberについてはunionの項目を参照されたし)、

というよりも、

もし、Xが、ユーザー定義のコンストラクターを持つ非staticメンバーを持つunionか、あるいはそのような無名unionを直接のメンバーに持つクラスの場合、

と書くほうが、参考書では分かりやすいだろう。

2011-01-21

Duke Nukem Foreverに学ぶこと

結論:PublisherとDeveloperが分かれているのは合理的である。Project Leader/Managerは必須である。

1996年に発売された、Duke Nukem 3Dは、非常に成功したゲームであった。1997年、3D Realmsは、続編の、Duke Nukem Foreverの開発に着手したことを発表する。その時の発売予定は、1998年の中頃であった。

Duke Nukem 3Dのために3D Realmsによって自社開発されたプログラムは、Buildエンジンと呼ばれていた。これは、当時、最高水準の描画をした。しかし、1997年に発表されたQuake IIエンジンは、さらに描画が良かった。そのため、3D RealmsのGeorge Broussardは、Quake IIエンジンへの移行を決定した。これは、これまでの作業の殆どを捨ててしまうようなものであった。

さて、1998年に、Unrealエンジンが発表された。これは、さらに描画がよかった。そのため、Broussardは、移行を決断した。

Duke Nukem Foreverの開発は、全く終わらなかった。何故ならば、Broussardが、どんどん機能の追加は修正を要求するからであった。社員の間でかわされるジョークとして、「Broussardに最新ゲームをプレイさせるな」というものがあった。Broussardが新しいゲームをプレイするたび、何かしら新機能の追加を要求するからである。

1990年代後半から、2000年代は、日々ハードウェアの性能が向上していく時代であった。常により高速で高機能なハードウェア、それを使った描画技術が発表されていた。Duke Nukem Foreverは、それらを延々と取り込み続けたのである。Broussardは、ゲームを最高のものにしたかった。しかし、それには時代の進歩は、あまりにも早すぎた。どこかで機能追加をやめて、テストや微調整をして、出荷すべきだったのだ。しかし、何時までたっても、Broussardは変更を要求し続けた。

一体、なぜこんなに馬鹿げた開発を続けられたのか。どうして資金が続いたのか。ゲームの開発資金というのは、どのゲームDeveloperでも、問題になっている。それ故、Developerはたいてい、Publisherから開発資金を提供してもらうのである。

ところが、3D Realmsの場合、Duke Nukem 3Dがあまりに成功しすぎたため、現金がうなっていたのである。Builtエンジンのライセンス料もかなり入ってきていた。そのため、3D Realmsは、自社の資金で、延々と開発を続けられたのである。

Publisherは、ゲームの発売日延期を嫌う。延期すると、それだけ余分に費用がかかるからである。一方、生粋の開発者というのは、ゲームをできるだけ素晴らしい状態にしたいと考える。それゆえ、開発期間は伸びがちである。PublisherとDeveloperの対立は、スーツと現場の対立に似ていて、珍しいものではない。

Duke Nukem ForeverにPublisherはいた。しかし、Publisherは、口をさしはさむことができなかった。なぜならば、開発は3D Realmsの資金で行われていたのである。Broussardは、いつDuke Nukem Foreverが発売されるのかという質問に対して、常に、「完成したとき」と答えていた。

今や、ゲーム開発というのは、途方もなく大規模になってしまった。何億、何十億という金をつぎ込み、50人、100人といった人員を雇うものである。ところが、Duke Nukem Foreverは、1990年代の精神のまま、少数で開発を続けていた。そもそも、最終的な完成がどういったものになるのか、Broussard自身にも、明確なイメージがなかったのである。ただ最高のゲームを目指して、延々と変更と機能追加が続けられたのである。

資源は有限である。3D Realmsも、ついに資金が尽きかけてきた。ここ最近の顛末は省略するが、まあ、あまりいい状況ではない。2011年に発売するといっているが、どうなることやら。

結局、Duke Nukem Foreverの教訓としては、ソフトウェア開発には、明確な納期が必要なのだろう。どこかで機能追加を凍結して、微調整して出荷しなければならない。また、全体を管理するProject Leaderも必要である。Broussardの仕変要求を最初にはねのけたのは、Brian Hookであった。

参考:
Learn to Let Go: How Success Killed Duke Nukem | Magazine
Duke Nukem Forever - Wikipedia, the free encyclopedia

2011-01-20

Old New Thing: なんでスペースシャトル使ってアポロ13号を助けなかったの?

Why didn't they use the Space Shuttle to rescue the Apollo 13 astronauts? - The Old New Thing - Site Home - MSDN Blogs

多くの出来事というのは、当時の時代背景を考えなければ成立しないものだ。

ゆとりの映画バカが、なんでNASAはスペースシャトル使ってアポロ13号の乗組員を助けなかったんだ、と疑問に思うごとく、今日のコンピューターユーザーは、昔の事情に対して、そもそも存在していなかった技術の存在を前提に考えようとする。

例えば、Windows 3.1にコンソールサブシステムがなかったということは、ipconfigをキャラクターモードのアプリケーションとして提供しない言い訳にはならないという意見だ。「ま、コンソールサブシステムがなかったとしてもだ、なんでDOSを使わないんだ

MS-DOSプロンプトというのは、MS-DOSを走らせる仮想マシンである。仮想マシンであるがゆえに、MS-DOSプロンプトは、あたかもMS-DOSそのものであるかのようにふるまう。現実には、もちろん、Windowsによってコントロールされているシミュレーターの中で走っているに過ぎない。しかし、シミュレーションの目的は、古いアプリケーションを、あたかもMS-DOS上で走っているものと信じさせて実行させることにあるのだ。

「Win 3.1にセキュリティなんて概念はなかったじゃないか。だったら、DOS上で動くプログラムがシステムにどんな影響を及ぼしたところで、別に構わないじゃないか」

MS-DOSプロンプトは、仮想マシン上で走っていたため、あらゆる挙動は、仮想マシンマネージャーの監視下にあった。もし、アクセス権限のないメモリーにアクセスしようとしたならば、例外が発生し、仮想マシンマネージャーによってハンドルされる。もし、特権命令を実行しようとしたならば、例外が発生し、仮想マシンマネージャーが、「いいや、限界だ。押すね」とばかりに、仮想マシンを停止させる。そういう意味では、MS-DOSプロンプト上で走るプログラムは、デスクトップ上で走るWindowsアプリケーションより、保護、隔離されていたということになる。何故ならば、Windowsは、各MS-DOSプロンプトごとに、別々の天地を作り出していたからだ。

仮想化における前提条件としては、MS-DOSプロンプト上で走るプログラムは、昔の普通のMS-DOSアプリケーションである。Windowsアプリケーションではない。MS-DOSには、Windows APIは存在しない。したがって、MS-DOSプロンプトにも、Windows APIは存在しない。WindowsをLinux上の仮想マシンで走らせておきながら、なんでWindowsプログラムがXCreateWindowを呼び出せないのかと疑問に思うようなものだ。XCreateWindowは、ホストシステムの関数だから呼び出せないのだ。仮想マシンには存在しない。

確かに、仮想マシンに穴をあけて、MS-DOSプログラムからWinSock APIを呼び出せるようにすることは可能である。

しかし、仮にそのようなはからいがあったとしても、やはり、MS-DOSプログラムのipconfigは欲しくないだろう。

思い出して欲しい。Windows 3.1には、二種類のモードがあったことを。スタンダードモードと、エンハンスドモードである。スタンダードモードは、80286プロセッサー向けの設計である。80286には、仮想メモリーや、仮想マシンのサポートなどといった機能はなかったのだ。MS-DOSプロンプトを実行したら、スタンダードモードのWindowsは、すべてのWindowsプログラムを休眠し、自分自身も休眠する。その後、MS-DOSプログラムを実行するのである。(もちろん、フルスクリーンである。なぜならば、ウインドウを表示させるべきWindowsは存在しないのだから)。しかる後に、MS-DOSプログラムが終了したら、Windowsは自分自身を復帰させ、MS-DOSプロンプトを開く前の状態に戻すのである。

単にコンピューターのIPアドレスを得るためだけに、一切の作業をやめ、Windowsを事実上シャットダウンし、ディスプレイをキャラクターモードに切り替える。その結果、たかだか16文字を画面に表示できるようになるというのは、実に馬鹿馬鹿しい次第である。

「んでも、スタンダードモードのWindowsなんてどうでもいーじゃん。エンハンスドモードだけで動くってことにしたらどうよ。エンハンスドモードなら、MS-DOSプロンプトをマルチタスクできて、Windows上で動かせるじゃん」

思い出して欲しい。Windows 3.1のエンハンスドモードにおける、最小メモリー要求は、1664KBであったことを。さらに、各DOS窓は、約1MBのメモリーを消費する。つまり、たかだか16文字を画面上に表示するために、コンピューターの半分以上ものメモリーが必要になるのである。

「えーと、サポセンは問題解決のために、俺のIPアドレスを聞いてきた。IPを調べるには、このプログラムを実行しないといけないな。そのためには、まず作業をセーブして、今開いているプログラムを全部閉じないと、プログラムを実行するだけのメモリーが空かない」

単にWindowsアプリケーションを使ったほうがいい。

ボーナスコメント:640kが、なんでwinipcfgはipconfigを呼ばないのかと質問してきた。

いいかね。「全く別物で互換性のないプログラムに、同じ名前をつけよう」。問題解決からますます遠ざかるだけだな。

64bitコードへ移行はすみやかに行われるのだろうか。

2011-01-19

USよりイランへのソフトウェア提供、解禁さる

Official Google Blog: Software downloads for Iran

USには、ソフトウェアを敵国(時には全外国)に輸出してはならないというアホ臭い法律がある。かつてアメリカで開発された暗号技術は、このためにアメリカ国外に輸出できないというアホ臭い問題を抱えていた。暗号方式の国際的な共通化は必須である。正しく設計された暗号は、暗号方式が分かっていても、解読が容易になるべきではない。

ところで、2009年のイランの選挙では、インターネットが重要な役割を果たした。結果としては、独裁政権が勝ったのだが、不正も疑われている。これを考えるに、インターネットは正しい情報を公開するための素晴らしい道具である。

このたび、この輸出規制が緩められ、GoogleはイランにGoogle Earth, Picasa, Chromeを提供できるようになったらしい。

もし、アメリカ合衆国が真に世界の民主化を望むならば、ソフトウェアの輸出規制などやめるべきである。

西山事件を未だに引きずっている日本も他人ごとではない。

Mark Russinovichが小説を書いたらしい

旧Sysinternalsで有名なMark Russinovichが小説を書いたらしい。その名も、Zero Day.

zerodaythebook.com

マルウェア暴走によって起こる人災の話らしい。

2011-01-18

WebMがオープンねぇ

Lair Of The Multimedia Guru » Chrome droppings

オープンねぇ、ITU H.264の開発は、JVT-expertsという、公開ML上で行われていて、公開FTP上にソフトウェア、ドラフト規格、提案された変更、テスト結果、会議その他あらゆるものが置いてある。当時、誰でもリアルタイムで議論や提案ができたわけだ。GoogleのWebMは、On2の動画コーデックである。私の知る限り、On2によってクローズドで開発されていた。でも、On2の人間はjvt-expert MLにもいたんだけどなぁ。

そういうわけで、WebMがH.264よりオープンだというのは、中傷でしかない。特許問題というのは、これからWebMが広く使われて、企業が、調査するだけの利益が得られると判断したときまで分からない。それに、高画質と圧縮率でどちらが優っているかは、すでに何度も結論されてきたではないか。Googleには新しいモットーが必要なのではないかな。例えば、"don't be stupid" みたいな。

コメント欄では、さらに面白い予想がでてきている。WebMは、OggやTheoraと同じく、ゲームに使われ、Webでは、結局Flashが引き続き使われるだろうという予想だ。なかなか面白い。

もっとも、ゲームではBinkの牙城を崩すことはしばらく無理だろう。Binkは圧縮率も画質も最悪のコーデックであるが、デコードのコストが低く、現在、およそゲームを動かすことができるすべてのプラットフォームで実装されているという強みがある。ゲームには、圧縮率や画質よりも、デコード負荷や移植性の方が重要なのである。

そういえば、VP3がH.264より早く公開されているという指摘があるが、後にH.264と呼ばれるようになった規格の制定作業は1998年から始まって、1999年に最初のドラフトが公開されている。先に公開されたのはどっちだろう。

2011-01-17

C++0xの新機能によってユーザーが受ける恩恵

どうも執筆がはかどらないので、気分転換に、C++0xの新機能によって、一般ユーザーのコードがどのように便利になるかを示してみようと思う。

問題:以下のような仕様の関数printを実装し、また実際に呼び出しなさい。
宣言:関数printは、std::vector< std::string > const &を仮引数に取り、std::vector< std::string >::const_iteratorを戻り値の型として返す。
本体:関数printは、仮引数のvectorの要素をすべて標準出力に出力する。
戻り値:v.cbegin()。

C++03の範囲(ただし、cbegin、cendあり)でこれを実装すると、以下のようになる。

std::vector< std::string >::const_iterator
print( std::vector< std::string > const & v ) 
{
    for (   std::vector< std::string >::const_iterator iter = v.cbegin(), end = v.cend() ;
            iter != end ;
            ++iter )
    {
        std::cout << *iter << std::endl ;
    }

    return v.cbegin() ;
}


int main ()
{
    std::vector< std::string > v ;
    v.push_back("foo") ;
    v.push_back("bar") ;
    v.push_back("hoge") ;
    v.push_back("moke") ;

    print( v ) ;
}

このコードは、何をしているのかさっぱりわからない。おそらく、一ヶ月後にこのコードを見なおしても、書いた本人ですら、まともに読めないだろう。C++0xの新機能を使って、このコードを読めるようにしてみよう。

まず問題なのは、std::vector< std::string >::const_iteratorなどという、べらぼうに長い型名だ。これを何とかしよう。それには、auto specifierとdecltypeが使える。

auto specifierは、変数の型を、初期化子から決定してくれる、大変便利な新機能である。decltypeは、型をdecltypeに渡した未評価式の型にしてくれる、これまた便利な新機能である。

// 新しい関数記法
auto print( std::vector< std::string > const & v )
    -> decltype( v.cbegin() ) // decltype
{
// auto
    for (   auto iter = v.cbegin(), end = v.cend() ;
            iter != end ;
            ++iter )
    {
        std::cout << *iter << std::endl ;
    }

    return v.cbegin() ;
}

だいぶマシになった。次は、for文だ。わざわざループのためのイテレーターのインクリメントや、終了条件の判定を、自分で書くのは面倒だし、間違いのもとだ。これには、range-based forが使える。

auto print( std::vector< std::string > const & v ) -> decltype( v.cbegin() )
{
    for ( auto elem : v )
    {
        std::cout << elem << std::endl ;
    }

    return v.cbegin() ;
}

これで、関数printは、実に短く簡潔に書ける。これで、一年後にコードを読んでも、スラスラと読めるだろう。

ところで、このprint関数を呼び出すために、vectorを用意するのが、これまた面倒だ。

    std::vector< std::string > v ;
    v.push_back("foo") ;
    v.push_back("bar") ;
    v.push_back("hoge") ;
    v.push_back("moke") ;

これには、initializer listが使える。

int main ()
{
    std::vector< std::string > v = { "foo", "bar", "hoge", "moke" } ;
    print( v ) ;
}

これも、POD型のクラスの初期化と同じで、非常に分かりやすい。

2011-01-14

fcloseはリソース解放を失敗しない

厳密に言うと、fcloseはエラーを返す可能性がある。しかし、それは、通常のプログラマーが考えるような失敗ではない。 。fcloseが失敗した後、プログラマがストリームに対してできることは何もない。

何故か。規格で明確に、fclose呼び出しが成功しようと失敗しようと、渡されたストリームは閉じられ、ストリームに関連付けられているバッファーも解放されることが保証されている。つまり、たとえfclose呼び出しが失敗したとしても、fcloseに渡したストリームは、すでに閉じられているのである。そのストリームに対して行えることは何も無い。

あるいは、無効なストリームを渡したという場合が考えられる。これは、無効なストリームをfcloseに渡したプログラマーの責任である。誰が失敗したかといえば、プログラマーである。fcloseの責任ではない。

まとめ:正しく実装されたfcloseは、リソース解放を絶対に失敗しない。たとえエラーを返したとしても、妥当なストリームを渡していた場合は、必ず解放される。

一般に、リソース解放というカテゴリーに分類されるライブラリは失敗すべきではない。たとえば、Win32 APIのCloseHandleも、同じ意味で、失敗はしない。CloseHandleに渡した妥当なハンドルは必ず解放される。

2011-01-13

吹いた

Chromium Blog: HTML Video Codec Support in Chrome

H.264のサポートをやめて、WebMにリソースを注ぐよ、というGoogleの発表を受けて、

An Open Letter from the President of the United States of Google - Tim Sneath - Site Home - MSDN Blogs

英語のサポートをやめて、エスペラントとクリンゴンにリソースを注ぐよ。

Old New Thing: 変な子のNOP

My, what strange NOPs you have! - The Old New Thing - Site Home - MSDN Blogs

僕のオフィスを掃除していたら、古いドキュメントを見つけた。Windows 95には、奇妙なNOP命令がそこらじゅうに使われていたのだ。

1987年以前に製造された80386を、B1 steppingという。この初期型の80386には、Windowsに問題を与えるバグがいくつがあったのだ。例えば、文字列命令(movs等)に続く命令が、別のサイズのアドレス(訳注:16bitなら32bit、32bitなら16bit)を使っている場合や(例えば、movs es:[edi], ds:[esi]に続いて、mov ax, [bx])や、続く命令が別のサイズのアドレスのスタックを使っている場合(例えば、 movs es:[edi], ds:[esi]を16bitスタックで行い、次に命令がpushの場合)、movs命令が、正しく動かない。このような細かい、「もし星の配置が正しければ」というようなCPUのバグは、結構ある。

ほとんどのCPUバグは、32bitコードと16bitコードを併用したときにしか発現しない。したがって、純粋な16bitコードや32bitコードならば、この問題に遭遇することはまずない。Windows 3.1は、16bitコードと32bitコードの併用はほとんどなかったので(ユーザーモードはすべて16bitで、カーネルモードはすべて32bit)、Windows 3.1では、この問題に遭遇することもなかった。

Windows 95は、しかし、ユーザーの16bit世界から32bit世界への移行のために作られたOSなので、かなり多くの併用が行われている。結果として、問題のあるコードシーケンスが使われることは、まれではなかった。

古いCPUをサポートするか、切り捨てるかという決断をしなければならなかった。市場を調査した結果、当時、それなりに多くのコンピューターが、80386を搭載しており、サポートする意義はあるように思われた。

アセンブリ言語でコーディングする開発者には全員、B1 steppingで問題が起こるコードシーケンスが伝えられた。そのようなコードシーケンスを発生させないようし、また、既存の問題のあるコードを見直すためである。手動での検証を助けるため、僕はWindows 95の全バイナリーに対して、問題のあるコードシーケンスが含まれているかどうか調べるプログラムを書いた。ツールでシーケンスが見つかるたびに、該当部分を調査し、問題があれば、その部分を担当する開発者に知らせた。

ほとんどのケースでは、問題のあるコードは、単にNOP命令の挿入で修正できた。問題が、「Xという命令に続くYという命令」であれば、二つの命令の間に、NOPを挿入して、「お開きにし」、問題を回避させるのだ。ただし、通常のNOP命令自体が、種類Yである場合があるので、Yにならないような特別なNOPを使う必要があった。

例えば、以下は色数フォーマットの変換を行う関数である。

push    si          ; borrow si temporarily

        ; build second 4 pixels
        movzx   si, bl
        mov     ax, redTable[si]
        movzx   si, cl
        or      ax, blueTable[si]
        movzx   si, dl
        or      ax, greenTable[si]

        shl     eax, 16     ; move pixels to high word

        ; build first 4 pixels
        movzx   si, bh
        mov     ax, redTable[si]
        movzx   si, ch
        or      ax, blueTable[si]
        movzx   si, dh
        or      ax, greenTable[si]

        pop     si

        stosd   es:[edi]    ; store 8 pixels
        db      67h, 90h    ; 32-bit NOP fixes stos (B1 stepping)

        dec     wXE

注意すべきこととして、昔のNOPは使えなかった。32bitアドレスのオーバーライドプレフィクス付きのNOPを使う必要があった。そう、これは通常のNOPではなく、32bit NOPなのである。

B1 stepping対策として、C言語を書く開発者は、比較的、苦労はしなかったが、楽でもなかった。苦労しなかったというのは、コンパイラーがコード生成を行うので、それに関しては特に心配する必要はなかったのである。楽でもなかったというのは、コンパイラーの開発者に、問題のあるコードシーケンスを生成してしまうCコードを、わざわざ教えてもらわなければならなかったのである。(For example, there was one bug that manifested itself in incorrect instruction decoding if a conditional branch instruction had just the right sequence of taken/not-taken history, and the branch instruction was followed immediately by a selector load, and one of the first two instructions at the destination of the branch was itself a jump, call, or return. The easy workaround: Insert a NOP between the branch and the selector load.)(訳注:taken historyのまともな日本語訳を思いつかなかったため断念)

また、B1 steppingnoのいくつかのバグは、簡単に避けることができた。例えば、B1 steppingは、最初の64KB分のメモリーに対しては、仮想メモリーをサポートしていなかった。まあ、そのアドレスでは仮想メモリーを使わなければいいだけの話だ。仮想メモリーが有効化され、ハードウェアのプリフェッチで、特定の状態になり、0x800000F8から0x800000FFの範囲のアドレスにアクセスする浮動小数点小プロセッサー命令が実行された場合、CPUは、0x000000F8から0x0000000FFの範囲のアドレスを読み込んでしまう。これも、簡単に回避できる。0x80000xxxにメモリーを割り当てなければいいのだ。2GB境界近くのアドレスは、触らずにそっとしておいたほうがいい理由がまたひとつできたわけだ。

私はたまたま、B1 steppingを搭載した古いコンピューターを、オフィスに持っていた。処理速度は遅かったが、ちゃんと動いた。たしか、テストチームの人が、Windows 95がB1 stepping CPU上でちゃんと動作するかどうかを検証するために、「接収」していった記憶がある。

製品開発の後期になって(最後のベータの後)、上層部は先の判断を翻し、B1チップをサポートしないことに決めた。たぶん、テスターがB1 stepping関連のバグを発見しすぎていたのかもしれない。たぶん、すべてのソースコードを検証し、さらに、B1問題をすべての開発者に教育するようなコストが、販売対象の顧客を少し減らす事による損益を上回ったのかもしれない。どういう理由にせよ、B1 steppingサポートは撤回され、古いチップを使っている顧客は、Windows 95のインストール時に、エラーが表示されるようになった。サポート部門の人の仕事を少し楽にするため、エラーメッセージ中のエラーコードは、Error B1だった。

当時、苦労したんだろうなぁ。

2011-01-11

何だってー!

1080. Confusing relationship between templates and copy constructors

まさに、これから書こうとしていた部分に対する変更だ。これは怖い。

C++0xでは、テンプレートなコピーコンストラクターは、コピーやムーブのためにインスタンス化されることはないと規定されていた。C++0xのドラフトの解釈に従えば、以下のようになる。

struct S
{
    S() { }
    // 暗黙のコピーとムーブコンストラクターが生成される

    // テンプレートなコンストラクター
    template< typename T > S( T ) { }
    template< typename T > S( T const & ) { }
} ;


int main()
{
    S obj ;
    S a( obj ) ; // 現行ドラフトの解釈では、暗黙のコピーコンストラクターが使われる(誤り)
}

この例では、テンプレートなコンストラクターがインスタンス化されることはない。

ところで、そもそもこれは本来の目的なのかという問題が提起された。そもそもの目的は、自分のクラス型を唯一の仮引数として取るコンストラクターを禁止するものである。

struct X
{
    X() { }
    X( X ) { } // ill-formed
} ;

X(X)というコピーコンストラクターは、ill-formedである。なぜならば、このコピーコンストラクタを呼び出すためには、Xをコピーしなければならない。しかし、Xをコピーするためには、このコピーコンストラクタを呼び出さなければならない。Catch-22問題である。故に、このような宣言はill-formedである。

また、この問題に関連して、以下のテンプレートのインスタンス化を防ぐものである。

template< typename T > S( T ) { }

これが、C++03時点での意図である。この形以外のテンプレートなコンストラクターは、問題なく、コピー(C++03にムーブはなかった)のためにインスタンス化される。クラス型自体はill-formedだが、クラスへのポインターやリファレンス型は、問題がない。

実は、T &やT &&な仮引数のコンストラクターは、C++0xのドラフトに、誤って付け加えられたのである。それが、取り除かれることになった。そのため、上記のコードでは、テンプレートのインスタンス化が起こり、テンプレート版のコピーコンストラクター(仮引数がT const &の方)が使われる。

問題は、まだN3225には、この変更が反映されていない。危ないところだった。これだから、今のドラフトはアテにならないのだ。しかし、よくもこんなドラフトでFCDが通ったものだ。

2011-01-10

C++のちょっと知られていないこと

他のクラスのメンバー関数を、friend関数にできる。

struct Y
{
    void f() ;
} ;

struct X
{
    friend void Y::f() ; // OK
} ;

このように、他のクラスのメンバー関数をfriend宣言することで、クラスすべてのメンバーではなく、あるメンバーだけを、friendとすることができる。

オーバーライドしたvirtual関数のアクセス指定は、オーバーライドされるvirtual関数には影響を及ぼさない。

class Base
{
public :
    virtual void f() { } // publicメンバー
} ;

class Derived : public Base
{
private :
    void f() { } // Base::fをオーバーライド、privateメンバー
} ;

int main()
{
    Derived d ;
    d.f() ; // エラー、Derived::fはprivateメンバー

    Base & ref = d ;
    ref.f() ; // OK、Derived::fを呼び出す
}

Base経由で、privateメンバーであるDerived::fを呼び出すことができる。これは、virtual関数のアクセスチェックは、呼び出す時点での宣言により、静的に決定されるためだ。関数mainから、Base::fにはアクセスできるため、ref.f()という式はエラーにならない。後は、通常通り、virtual関数呼び出しによって、オブジェクトの型である、Derivedのfが、正しく呼び出される。

もちろん、いま執筆中の参考書には、これらの仕様も漏れ無く説明されている。

2011-01-09

gccにバグレポートするべきか迷う挙動

以下のコードをgccでコンパイルしてみて欲しい。

struct Base { void f() { } } ;

struct D1 : Base { } ;
struct D2 : Base { } ;

struct Derived : D1, D2
{
    void f()
    {
        Base::f() ;
    }
} ;

weekly buildでは、結果は、「internal compiler error: in build_base_path, at cp/class.c:270」となる。バグ報告するべきなんだろうか。

ちなみに、MSVCでは、問題なくコンパイルが通ってしまう。これも問題だ。興味深いことに、MSVCは、派生の順番がアクセスチェックに影響するらしい

struct Base { void f() { } } ;

struct D1 : private Base { } ;
struct D2 : Base { } ;

struct Derived1 : D1, D2
{
    void f()
    {
        Base::f() ; // エラー、Base::fはprivate
    }
} ;

struct Derived2 : D2, D1 // 順番を変えただけ
{
    void f()
    {
        Base::f() ; // エラーなし。そんなバカな
    }
} ;

素直に実装すれば、name lookupの時点でエラーになるはずなのだが、それを通り越して、アクセスチェックでのエラーになっている。しかも、アクセスチェックが派生の順番に影響されるという不思議な仕様。MSVCは本当にクソだ。

うーむ

11.5 Protected member access [class.protected]がよく分からない。非staticデータメンバーがprotectedメンバーのとき、追加のアクセスチェックが行われるとあるのだが、どうもよく分からない。この文章がわざわざ存在する必要があるのだろうか。

2011-01-07

MSVCのバグだと思ったら、実はGCCの未実装だった

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

class Y ;

class X
{
    friend Y ;
} ;

MSVCでコンパイルが通り、GCCでコンパイルが通らなかったので、てっきりまた、MSVCのバグかと思ったが、規格を参照したところ、MSVCの実装はC++0xとして正しいものであった。あやうくMSVCにバグ報告するところであった。危ない危ない。もちろん、ただしくGCCの方にバグ報告しておいた。

追記:バグではなく、未実装なのだという。まさかこのページのExtended friend declarationsがこれを意味するとは思わなかった。また、MSVCも、C++0xを実装しているというより、独自仕様である。C++0xの規定するfriend宣言をすべて実装しているわけではない。

C++0xでは、type specifierがクラスの型を表す場合、friendクラスの宣言として扱われる。C++03では、friendクラスの宣言は、常にelaborated-type-specifierを使わなければならなかった。つまり、class-keyが必須だったのである。

// C++03対応版
class X
{
    friend class Y ;
} ;

もちろん、あらかじめ名前が宣言されていない場合は、C++0xでもエラーになる。

class X
{
    friend Y ; // エラー、Yは宣言されていない
    friend class Y ; // OK
} ;

ちなみに、type specifierがclass typeではない場合、無視される。これはどういう事かというと、例えば以下のようなコードが通るということだ。

template < typename T >
class X
{
    friend T ; // C++03ではエラー
} ;

X<int> x ; // friend宣言は無視される

この場合、friend宣言は、friend int ; という、意味のないものになってしまう。幸いにして、C++0xでは、単に無視される。その結果、コンパイルが通る。これにより、テンプレートコードが書きやすくなる。

コメントにも書いたように、C++0xでは、friendにtemplate parameterを使えるようになったが、それは、このブログの読者ならば、すでにご存知だろう。

2011-01-05

constexpr関数とテンプレートについて

Twitter / 妖精闇姫 非実在えつえつしーりつく: @kikairoya http://ideone.c ...
Twitter / 妖精闇姫 非実在えつえつしーりつく: template の行を削るとちゃんと(?)コンパイ ...
Ideone.com | Online C++0x Compiler & Debugging Tool

以下のコードを考える。

int g = 0;
 
template< typename T = void >
constexpr int f()
{
    return g;
}
 
int main()
{
    std::cout << f() << std::endl; // 0
    g = 1;
    std::cout << f() << std::endl; // 1
}

これは、一見すると、constexpr関数で、定数でもないstatic変数gを、constexpr関数の中から扱っているように見える。これはエラーにならない。しかし、テンプレートを外すと、エラーになる。何故ならば――

constexpr関数が、関数テンプレートのとき、テンプレートのインスタンス化の結果、constexpr関数の要件を満たさなくなった場合、その関数はconstexprではなくなる。

――からだ。インスタンス化が、constexprの要件をみたいしていないので、関数fは、通常の関数になる。もちろん、関数呼び出しは、定数式ではなくなる。たとえば、以下のようなコードは、エラーになる。

constexpr int x = f() ; // エラー、constexpr変数は、非定数式で初期化できない

2011-01-04

Coding Horror: Googleに問題アリ

Coding Horror: Trouble In the House of Google

Coding Horrorで有名なJeff Atwoodが、スパムサイト問題に言及している。

Jeff AtwoodはStack Overflowの管理人である。Stack Overflowは、プログラマーのための質問箱を提供しているサービスである。質問と回答は、サイトのユーザーによって行われる。

2010年度のstackoverflow.comのトラフィック元

88.2%ものトラフィックが、たったひとつのソースからやってくるというとき、そのソース元に感じることは・・・「危険」である。この考えは、プレゼントされた馬の口をその場で確認して馬齢を調べたりするぐらいに(訳注:慣用句、Don't look a gift horse in the mouth)、失礼なことかもしれない。あるいは、取引先の悪口を公然と言うとか。

それにしても、このアクセス解析を見ると、やはり、考え込んでしまうのだ。Googleは独占企業ではないとは、何度も聞かされてきたことだ。とはいえ、明らかにズバ抜けているのだ。もちろん、我々はいつでも、他のあまり役には立たない検索エンジンに切り替えることができる。東西、東西、自由の空気を吸いたまえ。

皮肉はさておき、私はGoogleを尊敬している。私の目標は、決して合併買収されることではない。物事には時間がかかるものだ。しかし、もし仮に、買収される企業を選ぶとするならば、Googleになるだろう。ソーシャルな繋がりより、情報の繋がりを重視する彼らの考えは、他のどの企業よりも、我々の目標と似通っているからだ。とにかく、Googleのもたらすトラフィック量には、大変満足している。しかし去年、なにか妙なことがおこった。パクリサイト(訳注:他のサイトのコンテンツをまるまるコピーして、大量の広告と共に転載するセコいサイト)の影響で、我々のコンテンツの、Googleにおけるランクが下がってきているのだ。

我々のコンテンツをパクること自体は、別に問題ではない。というよりも、むしろ積極的にパクるべきである。だいたい、ユーザーがサイト上に提供してくれているコンテンツの所有権を主張したり、コンテンツ搾取者になるというのは、フェアではない。Stack Overflowに書きこまれたこと、それから、Stack Exchange Networkサイトの書き込みはすべて、クリエイティブ・コモンズ cc-by-saライセンスで、コミュニティに還元されるのだ。コミュニティが成果を所有するのである。世界中の人間が、お互いに教えあい、質問と回答集から学んで欲しいためだ。改変、再利用、共有によって、教育がなされるのだ。それが我々の目標である。私はそのために、毎朝、目を覚ましているのだ。

しかし、この方法は、元ソースであるオリジナルの質問と回答が、一番上にランク付けされるという前提のもとに成り立っている。たとえば、Wikipediaだ。Wikipediaへのリンクをクリックしたら、中身は何もなくて、ただWikipediaの著作権を主張する文章と広告しか載っていなかったということは、あっただろうか。一度もないであろう。しかし、これは、現実にはありうる、ひどく汚いビジネスモデルなのだ。だからこそ、Joel Spolskyと私は、コンテンツにほとんど制限を加えず、コミュニティに還元することこそが正しいと信じているのだ。Googleは、単なるコピペアフィサイトに対し、ペナルティを課している。改変と再利用はいいのだ。しかし、単に丸々コピーしただけのアフィ乞食は、いただけない。

これは、一般常識に属すると思う。Googleのwebmasterコンテンツガイドラインにも、明確に書いてあることだ。

しかし、Web管理者の中には、ページランクを上げて閲覧者を増やそうと、まともなコンテンツがないのに大量の単語を列挙したページを作ったりする。Googleは価値のないパクリや自動生成ページによってランクを上げようと試みるドメインに対し、対抗措置を取る。

例:

パクリコンテンツ。あるWeb管理者は、他社のより価値のあるサイトから、コンテンツを持ってきて、ランダムにWebページのボリュームを増やそうとする。単なるパクリコンテンツは、たとえ価値の高いソースからのものであっても、サイト自体が、何らかの付加価値を提供しない限り、ユーザーには何の価値も与えない。オリジナルのコンテンツを作るのに時間を割いたほうが有益である。これは、閲覧者の再訪問に繋がり、よりよい検索結果をもたらす。

2010年、我々のメールボックスが、ユーザーからの不満で溢れかえった。不満というのは、至って普通のGoogle検索をしているのに、Stack Overflowからコピペして広告を追加しただけのパクリサイトだらけになってしまうということである。さらに悪いことには、オリジナルのStack Overflowの質問は、検索結果に上がらないことすらある始末。これは実に妙なことである。というのも、ライセンスは、質問の元ソースに対し、nofollowを使わずに、リンクし返すことという条件を付けているからである。パクリサイトをインデックス化するGoogleは、元ソースへのリンクを見逃すはずがない。このような自体に対し、コピペサイトからStack Overflowに自動リダイレクトするブラウザープラグインまで作られた。何というあほらしいことだろう。Joelと私は、これをありえないことだと考えた。これは、私の失敗のせいではないだろうかと思った。

Googleのせいだという意見は、私には妥当だとは思われない。GoogleはWebにおける万有引力である。揺るぎない不変定数である。Googleをそしるのは、万有引力を批判するのに等しい。そんなのは選択肢のうちにも入らない。私はまず真っ先に、自分のせいである(訳注:リンク先も同ブログ。プログラムの問題の責任は、まず間違いなくプログラマー本人にあるという法則)という法則に従った。我々は実に多くの微調整をwebmasters.stackexchange.comに対して行った。何かとんでもないアホ臭いことをしていないかどうかを調査した。スーパーハッカーのMatt Cutts(訳注:Google社員、この手のspamサイト問題への対策部署のトップである)も、私のtweetに反応して、検索結果を調査してくれた。両方に問題が見つかり、修正がなされた。成功だ!

結果として、多少の改善を見たものの、私にはふと、ある不安な考えがよぎった。極少数のコピペサイトによって、こんなにも効果的に、トラフィックを我々から奪えるのである。他のWebはどうなっているのだろう。私のGoogleに対する、万有引力定数のような信仰が揺らいだ。極めて根本的に揺らいだ。

この問題の調査中、私は始終、Googleの構築したアルゴリズムによる検索システムには、重大な亀裂が走っているのではないかという疑問を抱えていた。しかし私は、ド素人丸出しのバカ呼ばわりされるのを恐れ、この問題に関する記事を書かずにいた。そのような意見を広く公開するのは、あまり気持ちのいいものではない。なぜなら、問題の責任は我々にあるのかもしれないのだから。これは、我々のやり方である。万有引力が間違っているわけがない。我々のせいなのだ・・・だよね?

この数ヶ月、Googleの検索結果に重大な問題を抱えているのは、独り我々のサイトだけではないことは、無視することができなかった。事実、Googleの検索品質の低下を嘆く声は、ごく最近、頻繁に聞かれるようになったのだ。

個人的な意見では、私のパーソナル検索結果は、最近どんどん悪くなってきている。ワイフへのクリスマスプレゼント購入のため、私はGoogleで、「iPhone 4 ケース」と検索した。私はまったくもって無価値な最初の2ページを読み飛ばした後、Amazonでの検索に切り替えた。

私の信頼している知人も、皆、同様の意見である。Googleは、かつての最重要ツールとしてのGoogleは、次第に切れ味がナマってきているのではないか。スパマー、パクリ、SEO業者が、勝ってきているのだ。

まっとうな人間ならもちろん、この戦いではGoogleを応援するはずである。Googleには、是非ともアルゴリズムを調整してもらい、このブログ記事の内容を単なる杞憂にしてほしい。しかし、2000年よりこのかた、Googleの検索品質に疑問が生じたのは、これが初めてである。この疑問は、さらに飛躍した考えを生む。ひょっとしたら、我々はアルゴリズムによる検索が、戦略として敗北する、最初の兆候を見ているのではないだろうか。では次世代の検索は、アルゴリズム要素を弱め、ソーシャル要素を増やしたものになるのだろうか。

2011-01-03

Rubyと国際化と英語の問題

Lucas Nussbaum’s Blog » Blog Archive » Giving up on Ruby packaging

Lucas Nussbaumという人は、DebianのRubyパッケージの管理者なのだが、この度、引退するそうだ。やめるにあたって、興味深い内容の意見を出している。

Lucas Nussbaum, who is a Ruby package maintainer on Debian, wrote on his blog he will stop it. He also wrote interesting suggestion.

「Rubyの開発に関する議論は、多くが日本語で行われており、さっぱり分からない。Rubyの開発に関する議論は、すべて英語で行うべきである」というものだ。

That is, The development and discussion of Ruby is using Japanese. So he can't understand it. We should close ruby-dev@(japanese mailing list), and use ruby-core@ for any disucssion in English instead.

しかし、Rubyは日本発ということで、日本人の開発者も大勢いる。日本人のプログラマーの大半は、基本的に英語ができない。

However, since Ruby is from Japan, there are many Japanese contributors. Most Japanese programmers don't know English at all.

ちなみに、Matz本人によれば、「開発は基本的に英語で行われている。全ドキュメントとコミットログは英語である。英語は二級市民ではない。rubyに関する日本語のMLは、あっても問題ないだろうから、消すつもりはない」とのことである。

Matz himself commented on his blog.

今や、Rubyは国際的に使われている言語である。好むと好まざるとにかかわらず、国際的な開発がしたければ、現実的には、英語を使うより仕方がない。

Now, Ruby is used internationally. Whether you like it or not, international development requires, practically, to use English.

btw, I am Japanese. I think my English skill is better than most Japanese programmers even though it looks horrible and sometimes grammatically incorrect. Guess the rest. In fact, most Japanese programmers don't know English at all.

Learning the natural language is hard. You have limited time to learn things like programming languages, alogrithms, math, Physics, hardware etc... and English. What should we choose?

When I was a kid, I thought English is the most important skill for programming. I spent my youth studying English. Now, I'm not good at actual programming skills.

相変わらずMSVCは腐ってる

またMSVCのバグを発見した。using-declarationのunqualified-idが、基本クラスに対するtypedef-nameである場合、エラーになる。

struct Base { typedef Base type ; } ;
struct Derived : Base
{
    using Base::type ; // error C2886
} ;

ここまで来ると、参考書の一ページ目に、「MSVCは使わないこと」と注意書きしておきたくなってくる。