海野秀之(うんのひでゆき)の外部記憶
Twitter (twilog) / RSS / アンテナ / ぶくま
いい企画だなぁ。
自分が「親ばか期」なもんだから、 こどもが一生懸命しゃべっているのをみるだけでジーンときちゃうので、 かなり評価甘いめかも知れない。
アンタッチャブルも、うれしかったんじゃないかな。
ほんとは放送作家が書いたんじゃねの?って感じのもあったけど。
心は直接操作できないから。
ここに書かれているほどひどくはないんだけど、
くらいは、よゆーで当てはまる。仕事が好きで、いくらでも働けたころが夢のよう(あれがよくなかった説あり)。
で、やっぱ体鍛えるのが良いかなと思っていたところなんだが、食事には目がいってなかったなぁ。
ぼくは、べつに低血糖とかじゃない気がするけど、あきらかに炭水化物と砂糖はとりすぎていると思う。
ちょっと変えてみよう。
あと、「脳にいいことだけをやりなさい」って本、題が胡散臭すぎて、 ふつうに勧められても絶対読もうと思わないんだけど、ちょっと興味がわいた。
頭が働かない時にブドウ糖や甘いものを、と宣伝されているけど、 あれは砂糖業界の陰謀だとしか思えない。その一瞬はいいけど、すぐに悪循環に入ってむしろ悪化する。
陰謀論(笑)。でも、ありそう。即効性のある手段を常用したらだめなんだよね、きっと。
あと、ここに書かれている内容は、米業界にとってもつらそうだ。
低血糖を呈している人の経験談を、そのまま鵜呑みにしていいの?
だれの経験談であっても、鵜呑みはいけません。
あっち(自宅環境)でも、こっち(どこ?)でも GLib のバージョンが云々でインストール失敗して、 めんどうになったからというのは内緒だが、Cutter じゃなくて CUnit にしてみることにしたよ。
CUnitを使った開発の手順はおおむね次のようになります。
- 関数のインターフェイスを定め、ヘッダに追加する。
- 関数の仕様と定義されたインターフェイスを基にテストを書く。
- 定義されたインターフェイスに従った実装を書く。このとき関数内はほとんど空でいい。
- コンパイル/リンクし、テスト(実行)する。空の関数を呼び出すため、おそらくテストは失敗する。それによってテストされていること/関数が呼び出されていることを確認する。
- テストにパスするよう、関数を実装する。
ふんふん、なるほど。
doxygen もつかって、「1. 関数のインターフェイスを定め、ヘッダに追加する。」のときに、 ドキュメントも書いてしまうのがいいのかも。
だまって ./configure してみてダメだったから放棄っていうのも芸がなさすぎでした。
Cutter もインストールしてみる。さっき決めました(コメント欄参照)。
昨日、会社で先輩社員と交わした雑談の内容がちょっと印象深かったので、 メモっておこう。
他愛のない雑談で、稟議がどーの、予算がどーのと話していて、 「去年買ったあれだけど、金融ショックのあとだったら買えなかったよね、あぶなかった」と先輩。
そこで、僕は「でも、そんなのなんか変ですよね。『金融ショック』 で急に景気が悪化するとか…」と言ったんですが、その先輩いわく、 「いや、ぼくは、むしろ、急に変化してしまうのが『自然』 ってもんなんだと思う。」
そこで、僕もハッとした。
そうだ。マクロな系には相転移があるんだ。
そーか、そーか!
って、実は何にもわかっていないんですが、 なんとなく、すげー納得した。
そういえば、蟻の行列は渋滞しないんだってね。
月曜は子供と遊ぶために休んでいたため、今日になっちゃいましたが、Cutter インストールしました。 基本的に configure, make, make install だけで Ok の世界で「入れられませーん」とか、 どんだけ軟弱なんだ>自分
GLib, intl-tool あたりが、CentOS5 で yum ってこられるのよりも新しいのが必要だったかな、という程度。
さて、こんどのプロジェクト(最初はひとりプロジェクトさ!)では、Doxygen と Cutter を利用して、 TestFirst 初挑戦(←初だったのだ)の予定。いや、Doxygen も使うからには、TestFirst よりもさらに前にドキュメントかな。
Document first, Test second and Coding later
ってことで。
ぼくなんか、自分で書いたドキュメントが LaTeX で組版されて印刷されてくるのを眺めるだけで ニヤニヤできるクチできるなので、けっこう楽しいかもしれない。
一時、気の迷いで Doxygen + CUnit でいってしまえと思ったことがありましたが、 きっと Cutter を使った方が幸せなんだろうなと思います。 この点は、背中を押してくださった kou さんに感謝。
ふっふっふ(←楽しみなので、喜んでいる)。
ドキュメントが充実しているので、使い始めるにあたって苦痛はない。
ここでは、気づいたことをちょろちょろメモっていきます。だいたい、自分むけ。
もし、「これはバグかなぁ」とか確信がもてる(「かなぁ」なのに「確信」?)事柄が発生したら、 ご報告するようにします。
Cutter チュートリアル の最初の方は、GNU ビルドシステムの使いかたが書かれていたりします。 じっさい、autoconf とかつかったことなかった(はずかしい!)ので、ありがたい。
んで、
Cutterの使用
まずは、cutter.hを読み込めるようにする。Cutterはaclocal用のマクロファイルを提供している。そのため、容易にGNUビルドシステムから利用することができる。
まず、configure.acにCutterを検出するコードを追加する。
configure.ac:
... AC_CHECK_CUTTER
と書かれているんですが、AC_CHECK_CUTTER のところで怒られちゃいました。
/usr/local/share/cutter/stack/configure.ac を見ながら、AC_CHECK_CUTTER の代わりに
m4_ifdef([AC_CHECK_CUTTER], [AC_CHECK_CUTTER], [ac_cv_use_cutter="no"]) AM_CONDITIONAL([WITH_CUTTER], [test "$ac_cv_use_cutter" != "no"]) m4_ifdef([AC_CHECK_COVERAGE], [AC_CHECK_COVERAGE])
と書き直したらうまくいったような気がします。
これは、チュートリアルの記述が古いのかなぁ。ぼくんとこの環境の問題って気もする。
→わかった。やっぱり自分とこの問題だった。
% ACLOCAL_ARGS="-I /usr/local/share/aclocal/" ./autogen.sh
でうまくいったもよう。ま、なんか恥ずかしいので、くわしくは述べないが、 aclocal の設定がちゃんと出来てないじゃないかということですね。
実は、おうちの環境にはまだ cutter が入ってませんでした。
GLib いれたり、lntltool いれたり、xgettext いれたりして、 configure は通ったんだが、make でこけた。
cut-test-suite.c: In function `collect_backtrace': cut-test-suite.c:339: error: invalid lvalue in assignment cut-test-suite.c:341: error: invalid lvalue in assignment
該当箇所は、
339: stdout = pseudo_stdout; 340: g_on_error_stack_trace(cut_get_cutter_command_path()); 341: stdout = original_stdout;
ふむふむ。stdout に代入するのは、まかりならん、と。
よくわからんけど、dup とか dup2 とか使うのかなぁ。
よくわからんので、あとで。
"ld: fatal: relocations remain against allocatable but non-writable sections" は、もしかしたらポピュラーなハマりどころなのかも知れないと思い直して、再挑戦。
上の3つや、それらの組み合わせをいろいろ試してみたけど、改善せず。
もー、あれだ。relocation なんてするからいかんのだと、binutils を -fpic オプションつきでソースコンパイルしなおしてみる。
そうして、もういっかい cutter の ./configure してみたら、まだダメなんだけど、 ld の吐くエラーの数はだいぶへった。
気を良くして、./configure のときにも -fpic が付くようにすれば良いのではないかと思い、
% CFLAGS=-fpic ./configure
とやったら、いけたよ!やったぁ!
gmake -s check すると、いくつか fail していたりするんだけど、
% cutter/cutter --fatal-failures test FE 1) SystemError: cut-test-context-error-quark:0 失敗を致命的な問題として扱い、異常終了しています。 2) Failure: test_fail always fail fixtures/test-context/test-relative-path.c:10: cut_assert_always_fail(): cut_fail("always fail") fixtures/test-context/test-relative-path.c:16: test_fail(): cut_assert_always_fail() Finished in 0.006546 seconds (total: 0.000000 seconds) 1 test(s), 0 assertion(s), 1 failure(s), 0 error(s), 0 pending(s), 0 omission(s), 0 notification(s) 0% passed
always fail とか、わざと fail させているんだったら、問題ないのかな。
あと、途中で test がハングしたみたいになるんだけど、もしかしたら、遅いだけなのかも。 あんまり時間がとれないので、あとで確認する。
あ、test/run-test.sh は solaris の /bin/sh では動かないスクリプトだったので、 She-bang を #!/bin/bash に書き換えて使用。
昔どっかで読んだことあるんだけど、自分でやったことなかったので、やってみる。
/* duptest.c * dup/dup2 をつかって、標準出力を切り替えてみるよ。 */ #include <stdio.h> /* したの3つは open のため */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> /* unistd.h は dup/dup2 のため */ #include <unistd.h> int main() { int fd, stdout_backup; /* 標準出力のバックアップ * STDOUT_FILENO が指すファイル(つまり標準出力)を指すファイルディスクリプタを * もう一個つくる。 */ stdout_backup = dup(STDOUT_FILENO); /* 出力用ファイルを開く */ fd = open("hoge.txt", O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); puts("ひとーつ"); /* STDOUT_FILENO が fd と同じファイルを指すようになります。*/ dup2(fd, STDOUT_FILENO); puts("ふたーつ"); /* STDOUT_FILENO が stdout_backup と同じファイルを指すようになります。 * つまり、元通り標準出力を指すようになります。 */ dup2(stdout_backup, STDOUT_FILENO); puts("みっつ"); return 0; }
コンパイル、実行結果:
% gcc duptest.c % ./a.out ひとーつ みっつ % cat hoge.txt ふたーつ
うん。Solaris 10 上で確認した。
http://www.nurs.or.jp/~ogochan/essay/archives/1706
本当に放置するしかないのかとか、すべてに同意するわけじゃないんですが、結論には同意。
10年ほどまえ、じつは、 就職活動はいっしょうけんめいにやるもんだと勘違いしていたので(?)、 いくつもの会社の面接を受けたんですよね。
で、趣味とか聞かれるじゃないですか、なぜか。
で、正直に「散歩」とこたえて気まずい空気になったのを、思い出した(笑)。
いやー、どうやって話をふくらますのかのプランなく、
面接のひと「えーと、ご趣味は。。。(履歴書に「散歩」とかいてある)」
おれ「さんぽ」
両者「・・・・」
みたいな感じだったかと(若干記憶が脚色されている恐れあり)。
こりゃいかんなと反省した。
このたび書き始めたプログラムでは、Doxygen + Cutter を用いた Document & Test First な開発手法をためしている。
こりゃ、快適だ。
もちろん、上記は一本道ではなくて、header やドキュメントを書いているうちに、 インタフェースの不具合に気づいて書き直したりといった作業は当然発生します。 それに、開発対象すべてについて、1 が終わってから 2 というように進めるんじゃなくて、 ボトムアップにちょっとずつ作っていってます。
3. までの開発環境整備を、Cutter チュートリアルをみながらやった。 書かれているのを真似していけば、TestFirst かつ GNU ビルドシステムな開発環境が手にはいるよ。
やー、しかし、快適だなぁ。
いままでの僕はというと、
だったんですけどね(註:ぼくはソフト開発を業務にしていないので、アマチュアである点を強調しておきます。 一応、所属組織の名誉のために。)。
いやいや、すごい楽しい。このぶんだと、後になって「さいしょから書き直したい症候群」 を発症しないで済むかもしれない。
実は、autoconf, automake を使ってみるのも初めてだったのですが、 Cutter チュートリアルの通りにやってたら、自然とできちゃったというのも、ちょーありがたいです。
まじ、おすすめ。こーゆーの無しでプログラム書くとかありえない(ありえなかった人談)。
http://uhideyuki.sakura.ne.jp/uikitexi/index.cgi?cmd=pages
いつからくたばっとるんだ、きみわ。
調査……だけど、たしか、httpd のエラーログすら見られないので、手探りなんだよなぁ*1。 気がすすまん。
%ldd `which gosh` /**/**/bin/gosh: libgauche.so.0 => /**/**/lib/libgauche.so.0 (0x2807f000) libcrypt.so.2 => not found (0x0) libutil.so.3 => /usr/local/lib/compat/libutil.so.3 (0x281f0000) libm.so.2 => /usr/local/lib/compat/libm.so.2 (0x281f9000) libc.so.4 => /usr/local/lib/compat/libc.so.4 (0x28214000) libcrypt.so.2 => not found (0x0)
なんでしょね。libcrypt が見つからなくなったみたい。 ま、そのうち対処しよう。
なんか、レンタルサーバの OS が入れ替わったりして、環境が変わったんでしょ(←まじめに調べる気なし)ってことで、Gauche をもう一回ビルドしておわり。
はは。
微妙にバグっとるぞ。
ページの内容が途中で切れている場合がある。PDF で見ると全文見えるのに。
Content-Length をちゃんと出していない手抜きがたたったか。
*1 手探りではやってられないので、標準エラー出力をファイルに落とすようにしてあったんでした。忘れてた。で、今回は、それが空でした。つまり、エラーを吐くチャンスがないほど早い段階でこけていた。
% ls -l | less
みたいな場面で登場するパイプラインに対して、mkfifo なんかで明示的に作るパイプラインのことを、 named pipe とか言ったりすると思います。
じゃ、そうじゃないパイプライン (ls -l | less みたいなやつ) は名前の無いパイプラインなんだろうか。 それとも、ユーザが名前を意識してないだけ?
仕様として、どっかに書かれていないか調べたいんだけど、まずは実験してみよう。
コード:
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> main() { struct stat buff; const char *const name = "hoge"; int fdes; if (fstat(STDIN_FILENO, &buff) == 0){ printf("stdin: dev=%d, nlink=%d\n", buff.st_dev, buff.st_nlink); } mkfifo(name, 0644); if (stat(name, &buff) == 0){ printf("named pipe: dev=%d, nlink=%d\n", buff.st_dev, buff.st_nlink); } fdes = open(name, O_RDONLY); unlink(name); if (fstat(fdes, &buff) == 0){ printf("named, but unlinked pipe: dev=%d, nlink=%d\n", buff.st_dev, buff.st_nlink); } close(fdes); }
実行例(1):
% ./a.out stdin: dev=11, nlink=1 named pipe: dev=64769, nlink=1 named, but unlinked pipe: dev=64769, nlink=0
実行例(2):
% echo hoge | ./a.out stdin: dev=6, nlink=1 named pipe: dev=64769, nlink=1 named, but unlinked pipe: dev=64769, nlink=0
このときの stdin が、問題のパイプライン。やっぱり nlink が 1 になっている。 つまり、ぼくが名前を知らないだけ、だと思えばいいのかな?
シェルのソースを読めってことか。そういや、bash のソースでコードリーディングの練習しようと思ってたんだった。 (zfs もね)
なお、実行例はいずれも CentOS 5 上にて。別途 named pipe になにか書いてやらないと、open が刺さるらしい。
ためしに、Solaris 10 上でコンパイルしようとしたら、
In function `main': 24: error: `O_ASYNC' が宣言されていません (この関数内で最初に利用) 24: error: (未宣言の各変数については、それが最初に現われたそれぞれの関数 24: error: に対して一度だけ報告されます。)
だってさ。要調査。
→ O_RDONLY に変えた。ってか、O_ASYNC が何かわかってない(笑)。
Solaris 10 でやってみた:
% ./a.out stdin: dev=90701824, nlink=1 named pipe: dev=67174447, nlink=1 named, but unlinked pipe: dev=67174447, nlink=0 % ls | ./a.out stdin: dev=92536832, nlink=0 named pipe: dev=67174447, nlink=1 named, but unlinked pipe: dev=67174447, nlink=0
おおおおおおおおおおおおおおおっ!!!!!!!!
ほんとに、unnamed (unlinked) だ!他人は何人たりとも触れないところ(おおげさ)に居る!
いやぁ、Linux と Solaris 見比べると面白いなぁ。
シェルの実装の違いだったりして。 /proc の仕様がちがうからって線もありそうだ(まったく確信・確証はない)。
ぼくにとって、はじめて使う単体テストフレームワークが Cutter なので、 他のと比較できないんですが、噂に聞いていた CUnit の問題点はいくつもクリアーされているみたい。
やっぱり、Cutter にしてよかった。
テストの追加が容易(test_hoge って名前にしておくと、勝手に追加される、とか)というのは、 超重要。テストを書くのが面倒臭いようでは、いけない。CUnit がどうだったのかは、知らないんだけど。
で、ちょっと気にしていたのが、 C言語におけるTDDの問題点と解決方法 に書かれている問題だったですが……。
Cutter では、かなりスマートに解決されているみたいです。 Cutter を用いたテストでは、 テストコード、テスト対象コードともに、共有ライブラリとして Cutter からリンクされます。 どの関数をどういう名前の共有ライブラリにまとめるのか、とか、テストでリンクするライブラリとかは、 Makefile で自由に制御できるところがミソかな。 ここで、きちんと考えてやれば、スタブの競合のような問題はすんなり、スマートに回避できます。
テストのためだけの #ifdef とかを開発物中に一切書かなくても良いってのは、かなりポイント高いと思う。
Linux 上で、ruby-1.8.7-p72 を configure, make した場合、File.flock の実装に flock(2) が使われるみたい。
Solaris 10 上でビルドしたやつをみると、configure 時に checking flock が no になって、 missing/flock.c が使われていた。結果、fcntl(2) が使われる。
http://www.linux.or.jp/JM/html/LDP_man-pages/man2/flock.2.html によれば、 Linux の flock(2) は NFS 上のファイルをロックしないそうだ。
これ……、ローカルファイルシステム上で動作確認した Ruby プログラムが、 NFS 上ではきちんと動かなくなったりするってこと?
ふうむ。
このへんをみるに、 Solaris で checking flock が no になったのは、configure を実行したディレクトリが NFS 上にあったのと、SunOS における flock の実装(NFS 上では失敗してエラーを返す)が効いているよう。
Linux はどうかな、って、個人的にはどうでもいいんだけど(苦笑。 気が向いたら実験してみよう。
むかし、~/html/wiki/ あたり(NFS マウントされている)に設置した Hiki のファイルが時折ぶっこわれた理由がこれだったり、しないよね?
Before...
# うんの [捕捉ですね。いつもやる typo。]
# うんの [> ちなみに、GLibのバージョンはいくつですか? 職場の、ぼくの手元にある環境は CentOS5 でして、yu..]
# kou [おぉ!やったぁ! 2.12ですか。正規表現とdiffのサポートを抜けばいけるかしら。 でも、diffがないとCut..]