著作一覧 |
VC#の本と出版時期が同じだからだろうけど、アスキーからもらったので読んだ。えらくおもしろい。
Windowsプログラミングの極意 歴史から学ぶ実践的Windowsプログラミング!(Raymond Chen)
どのくらいおもしろいかといえば、
例)行末文字がCR+LFなのはなぜか
RFC0821……すべてCR+LFを行末シーケンスとして指定していることがわかる。
これは正しい。NVTの仕様だからだ。
そこで、実際の問題は、「なぜCP/M、MS-DOS、Win32がCR+LFを行末文字として使用するのか」ではなく、「なぜほかの人はこれらの仕様書に逆らい、ほかの行末文字を使用することにしたのか」である。
で、考察。
あるいは、
例)Altキーを押すとキャレットが点滅しなくなるのはなぜか
これは、「ファイル名を指定して実行」での話だが。
というような、Windows3.0あたりからの(行末コードについてはCP/Mからなわけだと言いたいことが上の引用からわかる)永遠の互換性に縛られた世界で、リバースエンジニアリングしてハックしまくっているアプリケーション群のために互換性を確保してやったり、「おとりの[画面のプロパティ]」を用意してやったり、いんちきベンチマークの間引きに対処してやったり、Windows98のCDにTweakUIを付けてやったらサポートがえらくたいへんになったのでSP2から外したりとか、なんでも悪いのはマイクロソフトのせいにするライターとか、タスクバーを知ったかぶってトレイと呼ぶ(他のチーム、マスコミ、ユーザー、ISVのデベロッパー)連中を罵ったり、嘲ったり、嘆いたり、まあ大変である。
だが、おまいらがExcel専用のAPIをこっそり用意して(しかも隠していたり)、最初にプリロードをして起動速度を稼いだりしてたのは、知っているのだが(いや、それは私のせいではないという声もどこかで聞こえた気もするが……というように曖昧にMSのせいにする人たちを呪ってみたり)。
というわけで、なにがプログラミングの極意かといえば、C++の多重継承をした場合のvtblが、複数のインターフェイスを持つCOMのオブジェクトのレイアウトや、その多重継承のどのvtblを選択するかのずらしかたといった技術ネタも少しばかり混ぜながら(スタックを追っかけてコードのエビデンスを調べるのがばかげているということを書いている部分はおもしろい)、悲憤慷慨しまくるとても愉快な本。
成分分析すれば、
・ISVのデベロッパーがくずなせいで、おれたちが困る。10%
・ISVのデベロッパーがくずなせいで、おれたちが悪モノ扱いされる。10%
・ISVのデベロッパーがAPI説明書を読まずに、アルファ版のサンプルをそのまま利用しているので、おれたちが困る。5%
・マスコミがくずなせいで、おれたちが悪モノ扱いされる。10%
・ユーザーがばかなせいで、おれたちが悪モノ扱いされる。10%
・ユーザーが知ったかぶりなせいで、おれたちが困る。10%
・他のチームがくずなせいで、おれたちが困る。10%
・前任者がくさった仕様を残したせいでおれたちが困る。10%
・NTの連中が変なこだわりを持つから、おれたちが困った 5%
・ほかのOSの連中がどろぼうだから、おれたちは騙してやった 2%
……
・こんな技術を使っているから、参考にするといいよ。1%
というような感じ。ただし、役には立たないが、どうして困るのかの解説は基本的には技術的な話(↓のような例外もあるけど)なので、滅法楽しい。理屈がある分だけ、ただの悪口と違っておもしろさ100倍増。
Lunaの壁紙は最初、赤い砂丘をデフォルトにしようとしたら、尻に見えるというクレームがついたので、急遽差し替えた、本当にユーザーはくずだ、というような話とか。
つまり、技術書というよりは、Windowsの虚々実々をネタにした、良いAPIや良いUIはどうあるべきかについての本だ。というわけで、ジョエルの本に近い。ただし、経営的な観点はまったくないけど。
なぜ、CoTaskMemAllocではなく、SHGetMallocというのがあるか、どう内部的に使われているのか、といったくだらないアイディア(当時のPCは4Mしか積んでいないとか言い訳てんこもりだが、ここらへんではCOMチームがばかだから後先考えずにばかでかいライブラリを作りやがったというような罵り(もちろん、どの場所でも直接罵ってはいないが、眼光紙背に徹すればそこらじゅうにでっかな文字で「おれたちは悪くない」と書いてあるのが読める)がないのがちょっと不思議。せいぜい「OLE32DLLをメモリに読み込んでさんざんな目にあっていた。こうした厳しいメモリ状況では、4KBのメモリを失っただけでも目立った。」と軽く流している程度)について得々と説明してみたり。まあ、そうやって4MBのマシンの8MBをOSが使っているせいで、実際におれのようなISVの開発者がどれだけ楽しいプログラミングができたかについては、その楽しみを罵ることで報いてくれているわけで、(今となっては)実に愉快だ。
Swingアプリケーションのように、積極的にJavaのThreadを使いまくる必要があるときは別だが、Rjbをつなぎとして利用する方法がある。
いや、もちろん、RubyとJavaのつなぎ(ブリッジ)なのだから利用する方法も何も、そのもののように読めるかも知れないが、それは空間上のつなぎの意味で、ここで言うつなぎは、時間上のつなぎだ。つまり、JRubyがより高速化されるまでのつなぎ、という意味。
hfuさんの次のエントリーは、同じプログラムのRjb版とJRuby版について書かれたもので、両者の使い分けの参考になる(どうもありがとうございます)。
CRuby + Rjb + Geotools は JRuby + Geotools に比べて2倍くらい早い場合がある
同じような例として、フィボナッチ音楽(JRubyで動くかどうか試してないので嘘が書いてあるかも知れないけど)では、rjbのrequireに成功するかどうかで使い分ける例を示している(2007-08-08 _ フィボナッチミュージック(続))。
これらを見ると、RjbとJRubyの相違は、利用したいJavaのクラスのロードの部分に差が集約されていることがわかる(ロード後の動作で異なるソース記述が必要となる場合については、指摘していただければ対処します)。
フィボナッチミュージックのRjb/JRuby切り分けの仕組みをhfuさんのプログラムに適用することを考えてみる。
JRubyの環境では、rqeuire 'rjb'は失敗し、CRubyの環境では、require 'java'が失敗することを利用する。ここでは、JRubyまでのつなぎなので、主となるライブラリをjavaとしよう。
まず、直接これらをrequireする代わりに、環境に応じてJavaモジュールをセットアップするアプリケーション固有環境設定ライブラリに処理を切り出すことにする。
ここでは、そのプログラムをgeoinit.rbという名称にしてみよう。(9:54修正。なんか勘違いしてた)
#geoinit.rb begin require 'java' class FeatureReader module Java include_class 'java.io.File' include_class 'org.geotools.data.shapefile.ShapefileDataStore' end end class FeatureWriter module Java include_class 'java.io.File' …… end end rescue LoadError require 'rjb' class FeatureReader module Java File = Rjb::import('java.io.File') ShapefileDataStore = Rjb::import('org.geotools.data.shapefile.ShapefileDataStore') end end class FeatureWriter module Java File = Rjb::import('java.io.File') …… end end end
そして、元のプログラムでは、
require 'geoinit'
として、どちらを利用するかの詳細については隠ぺいする。
まとめると、Rjbを利用するか、JRubyを利用するかを、環境に依存した特殊な処理と考えることで、差の部分をアプリケーションのメインの処理から分離し、隔離する。アプリケーションのメインの処理では、どちらが利用されているかについて依存させないようにする。
このとき、Rubyの場合、オープンクラスという特徴を利用して、特別なラッパオブジェクト(プロクシ)は作らないですませるようにする。単に分離した初期化モジュールの中で、クラス定義式を直接記述できるからだ。このようにしておけば、独自のRjb/JRubyラッパが不要となるので、プログラムの見通しと風通しが良い状態に保てる。
ゴドーを待ちながら (ベスト・オブ・ベケット)(サミュエル・ベケット)どうも、上のリストを眺めているとDRYじゃなさ過ぎで気分が悪い(hfuさんのコードではなくて、おれのコード)。
次のようにしたほうが良いな。
#geoinit.rb begin require 'java' def jimport(cls) include_class cls end rescue LoadError require 'rjb' def jimport(cls) nm = cls.split('.').last module_eval "#{nm} = Rjb::import('#{cls}')" end end class FeatureReader module Java jimport('java.io.File') jimport('org.geotools.data.shapefile.ShapefileDataStore') …… end end class FeatureWriter module Java jimport('java.io.File') …… end end
jimportメソッドは、JRubyの場合は、include_classメソッドで、Rjbの場合であれば、与えられた完全修飾クラス名のクラス名を定数として指定されたクラスをimportする。
ジェズイットを見習え |