crb開発記

crb

「あおきさん今すぐirbを立ち上げて計算やってもらえますか?」

こんなメッセージをfenrirくんから受け取ったのが発端だった。

--

現代のPCは電卓が進化しすぎちゃったものなのでいわゆる「電卓アプリ」はWindows標準のモノから各種widget類までいろいろあるんだけど、どれも操作性という点でいまいちピンと来ない。
ひとつには「スタートメニュー」からわざわざずるずるとマウスポインタを動かすというのがどうにも機械に使われてるように感じさせてしまうからだ。
もうひとつには、美しいウィジェット型の電卓は、美しいんだけど実用的じゃないということもある。
実用一点張りという点じゃExcelでも立ち上げればいいんだろうけど、これまたオオゲサな気がして、結局ちょっとした計算のためにUSB電卓を買ってしまったりするわけだ。

そんなこんなで、プログラマがちょっとした計算を行うときには、irbのようなインタプリタ内蔵シェルを使うことが多いんじゃないだろうか。こんな記事も見かけた。
便利な方法なんだけど、いちいちそのためにコンソールを一枚立ち上げるというのがイケてない。

その昔、CBEVというプログラムがあった。
これは、クリップボードに入っている文字列を数式として評価するというもので、なかなか便利であった。
クリップボードを使うというのがなんともWindows的に理想に近い汎用的なインターフェイスで、これはイイ!と思っていたプログラムだ。ホットキーでポップアップよりも他のアプリケーションとシームレスでいいと思う。
先ほど検索してみたのだけど、今じゃダウンロードできなくなってるのだろうか。

--

「それならいいアイディアがあるんだよ。クリップボードを経由してデータ受け渡すようにするの」
「いいですねそれ。宣伝しますよ」

--

ということで作ってみた。
名づけてcrb(Clipboard Ruby)。
こちらからどうぞ。

--

実際に作ってみたらものすごく便利だ。
なにしろRubyのパワーが、Windows上のプログラムならどこからでも利用できちゃうんだから、可能性は単なる電卓にとどまらない。
たとえば数式処理をやらせてみよう。こちらからpoly-rubyをインストールしておく。
文字列化に "inspect" を使うとうまくいかないので、右クリックメニューから "to_s" を選んでおこう。

require 'polynomial'
f=Poly("(2x+1)(x^3-1)^2")
g=Poly("7x^2+6x+5")
h=Poly("3x^4")
(f+g*h)


これを選択してCtrl+C、あとは選択解除してCtrl+Vだ。

2x^(7)+22x^(6)+18x^(5)+11x^(4)-2x^(3)+2x+1


アプリケーションを選ばないというのがいいと思うのだがどうだろう。
上記の計算もブラウザ上で行ったものである。

(Dec. 7, 2006)

混ぜるな危険

>> すごいにゃー
> 使ってみた?
いえいえ、win版Rubyをインストールする気がさらさらないので。
ソースくれれば、Cygwin版したてますよん


fenrirくんから来たこのメイルでちょいブチ切れそうになる。
Windowsでrubyを使うならmswin32版だろ!と言いたい所だけど、ここら辺を見てもらえば判るとおりmswin32版rubyはなかなかにkludgeだったりする。
それは理解できるしcygwinのひとつもサポートしないようじゃ天下は取れないので(自分のホームページにも「cygwinの入ってないWindowsなんてジョークだ」なんて書いちゃってるし)、ちゃっちゃかちゃ〜♪とcygwin版に対応することにした。

これがハマり道の始まりだった...

--

crbはrubyの本体であるシェアドオブジェクト、msvcrt-ruby18.dllを呼び出すことで式の評価を実現している。
シェアドオブジェクト(Windows流に言うならダイナミックライブラリだな)の機構はmswin32版だろうがcygwin版だろうが Windowsカーネルの機能を利用しているので、使い方はまったく同一である。上記DLLの代わりにcygruby18.dllを呼び出せばいいだけ だ。
同一バイナリで両対応しようとするとちょっと大変なので、インポートライブラリを作ってふたつのバイナリを作ることにした。リンクするインポートライブラリを変えるだけで実現する。はずだった...

--

はずだったんだが、実際にそうしてビルドしてみると、これがばっちり動作しない。
調べてみると、cygruby18.dll内の函数をコールしたときにNull Pointerアクセスを行っている。たぶん、cygwin1.dllの初期化がうまくいってないのだ。
そんな馬鹿な、と思いつつ、そういえば以前からBorland C++Builderとcygwinとの混在するバイナリを作りたかったことを思い出す。C++BuilderはGUIを作るには便利だがコンパイラのサ ポートする文法が古いのでSTLなんかを使いたいときにはちとツラい。そういうときに、処理が面倒になる部分はcygwinのgccで、GUIの部分はC ++Builderでと使い分けられると便利なのだ。
#よく考えたらmingw32を使えば済むことなんだけど...

cygwin1.dllの初期化がうまくいってないのなら、DLLのエントリポイントを強制コールしてしまえばよい。この辺を読んでもそのような方法で対処していた形跡がある。
そこでcygwin1.dllのエクスポートしている函数を調べてみたのだが... 上記リンクの内容とだいぶ様相が違う。cygwin1.dllのナカミはバージョンによってまったく違っているという噂を裏付けるものだった。
途方に暮れつつも初期化函数とおぼしきルーチンをコールしてみた。しかし... 固まってしまったり落ちてしまったりでまったく事態は改善されない。
リンク先をたどってみるとこんな記事もある。インポートライブラリを使わずに実行時にLoadLibrary()で読み込めば大丈夫だというのだ。
この方法を取りたくなかったのは、ruby.hの内容と不整合が生じるからだ。rubyの内部にアクセスするためにはruby.h内のマクロがどうしても必要だし、ruby.hをインクルードしてしまうと静的な名前解決が必要になってしまう。
仕方がないので使っている函数だけ別名を作ってLoadLibrary()で引っ張ってくるようにしてみた。これはこれで利点もありそう(mswin32版とcygwin版とで同一バイナリにできる、とか)なんだけど、結果はインポートライブラリを使うのと同様だった。

これでうーんと考え込んでしまったのだが、どうやらcygwinはVisualC++などで作られたDLLとの混在をサポートしようとしていた形 跡があるのに気づいた。初期化が必要ならDLLのエントリポイントで行えばいいだけの話なので、たまたま手元のcygwin1.dllがバグってるのかも しれない。
そう思ってcygwinをアップデートしたところ... なんとあっさり動作するではないか。
なんだったんだよ一体。

--

ということでかなり拍子抜けの結果だったのだけど、cygwin版をサポートしたcygcrb.exeを同梱してみました。こちらからどうぞ。
上記のような事情で、cygwin1.dllのバージョンによっては動作しないかもしれない。

(Dec. 8, 2006)

cygcrbだが、ruby_init()で固まったり固まらなかったり。
該当部分を切り出してコンソールアプリにするとそこは通過するんだが現状のcrbではそこで固まってしまう。ううむ不可解。
デバッガで追っかけてみると、Init_stack()内でgetrlimit()を呼んでいるあたりで無限ループにはまっているっぽい。
しかしcygwinのソース見てみると、そもそもループがないんだよなぁ。

こりゃあ諦めた方がいいのか?

ということで現状の解決案は3つ。

1. Pure SDK with Cで書き直す。
2. Pure Rubyで書き直す。
3. このままハマりみちくねくね...

1. が一番簡単そうだけどこれからcrbを機能拡張したかったら今後ネックになること必至。$stdoutの書き換えとかやりたいんだけどそうなるとGUI作りまくりになるし。
2. はそう簡単ではなさそうだ。C+SDKで書かれたコードに直す方がまだラクだし。
3. は敢えて選択するのもアホらしいんだけど上にも書いたとおりcygwin(ってゆーかgcc)のコードとBorlandC++のコードを混ぜてみたいという野望があったりするのでそれはそれで意味があるのかなとか。でも当座やるべきことじゃないよなー。

あ。まだ選択肢はあったぞ。

4. fenrirくんに無理やりmswin32版rubyを入れさせる(笑)
5. gccでもコンパイルできるRAD環境をスクラッチする。

ワンクリックインストーラとか作ってもイヤ?>fenrirくん

5. に走りそうな自分がちとコワイ...

(Dec. 11, 2006)

ごめん高機能化への魅力に勝てずcygwin版サポート停止。
解決策がわかったら復活させたいけど。

コンソールサポートしてみました。

(Dec. 13, 2006)

ダウンロードはこちらから


ご意見・ご感想、お待ちしてます。
e-mail:shig@esprix.net

駄文のページに戻る

ホームに戻る