PFU Happy Hacking Keyboard Professional2 墨 英語配列モデル レビュー


PFU の「Happy Hacking Keyboard Professional2 墨 英語配列モデル」(以後、HHKBと記載します)を購入し、何週間か使ってみたのでレビューを書いておきます。

値段

価格.comで調べたところ、Amazon.co.jp が最安値だったため、そこで 18,634円で購入しました。HHKBは、Control キーが A キーの左側にある、いわゆる「UNIX配列」のキーボードです。ジャンパスイッチにより切り替えができるキーボードは他社製でもいくつか出されていますが、デフォルトでこの位置に Control キーが配置されている数少ないキーボードです。
一定の需要があるにも関わらず、市場の絶対数が少ないためか、数ある実用的なキーボードの中でも最高クラスのお値段で流通しています。価格.comの販売価格の推移を見てみると、平均価格は21,000円から24,000円の間で変化していることが分かります。

私は元々、同シリーズの廉価版「Happy Hacking Keyboard Lite2」を使っていたので、HHKB の Professional 版はある種の憧れでもあり、ここ2年ほど Amazon のカートに入れたり出したりしていました。そして先日、ようやく決心が着いて購入したところであります。

HHKB は耐久性がとても良いので、他のユーザの方もおっしゃってるように、10年以上使えるようです。10年で2万円弱…1年で2000円と考え、4,000~5,000円の安いキーボードを10年の間に何度も買い替える事を考慮すると、良い選択じゃないかと考えています。

プログラマーな自分は、1日の業務の大半はキーボードを叩いているわけですし、キーボードを叩くのが仕事と言っても過言ではありません。
車が趣味でホイールやシートに10万も20万もかけるという話でもないので、購入の決心が着きました。(車が趣味の方、ごめんなさい)

結論を言うと、キーボードとしては確かに割高ですが、得られる効果は大きなものだと感じています。

UNIX配列

thumb_pdkb400b_l

 

HHKB の特徴をまず挙げるとすると、やはりUNIX配列だと思います。IBM/PC 配列で言う CapsLock の位置に Control があります。しょっちゅう使うキーが左手の小指に最も近い位置に鎮座してくれていると、非常に効率的に作業ができます。UNIX 系のツールでは、特に Control キーを酷使して使いますが、IBM/PCの配列では C-y (Emacsのヤンクコマンド)など、いくら手が大きくても指がつってしまいそうです。あんまり正確な記憶ではありませんが、確か昔の Macintosh のキーボードもこの位置に Control キーがあったと思います。(Mac では Control よりも Command/Apple キーを多用する為、あまり真価を発揮できていないようですが…)

先述のとおり、これまでは HHKB Lite2 を使っていたのであまり違和感なく移行することができました。Lite2 との違いは、Fn キーが標準では右下の一つだけになっているところでしょうか(DIPスイッチで左のAltまたはSuperキーをFnキーに割り当てる事は可能です)。F1~F12 や PrntScr キーなどの特殊キーは Fn キーを使って押す必要があります。

英語配列

これまでずっと日本語配列に馴染んで来たのですが、今回初めて英語配列にしていました。英語配列を利用する利点は、よく言われるように「プログラミングに向いている」点にあるかと思います。ここから少しだけ掘り下げて、英語配列のメリットとデメリットについて触れてみたいと思います。

英語配列のメリット

キーが少なくて済む 60キーしかありません。キーボードがすっきりして、同じ面積でもキーを広く取れるメリットがあります。特にスペースキーの両サイドにある「変換」「無変換」キーがない為、誤タイプの危険性が低下します。

シンボル(記号)キーの位置が自然 ブラケット([]や{})が隣り合っている点やセミコロン&コロン、クオート&ダブルクオートがそれぞれ同一キーになっているなど、シンボルキーの位置がより自然になっています。また、数字キーを Shift キーを押しつつ入力するシンボルキーも日本語配列のように ASCII コード順にただ並べただけではありません。日本語配列では、数字の 0 キーには Shift キーを押した場合の記号は割り合てられていませんが、英語配列ではそのような無駄もなく、全てのキーに Shift キーを押した場合にシンボル(または大文字)が入力できるよう割り当てられています。さらに、日本語配列のキーボードには「¥」と「\」キーが独立して準備されており、どちらを押下しても結局はバックスラッシュ入力ですが、英語配列では「\」キーのみで、無駄がありません。また、日本語配列では Shift キーを押さなければタイプできない「」も Shift キーを押す事なく入力できます。代わりに「@」を入力する場合に Shift キーが必要になります。もし、シェルスクリプトや Perl を日々使う人であれば @ キーより キーの方が多用しているはずですので、Shift キーをタイプする手間が省けるのかもしれません。

エスケープキーの位置 「半角/全角」キーがないので、エスケープキーが Tab の直上、数字の 1 キーの左側に収まっています。私は Windows でも SKK(SKKFEP) を利用しているので、「半角/全角」キーは不要です。不要なキーがなくなった代わりに、エスケープキーがホームポジションに近づいてくれるおかげで、あまり手を動かす事なく、使用することができるようになります。(そもそも半角/全角キーって、「互換性を無視してまで、必要とされる機能毎にハードウェアを追加する」みたいで、あまりクールな方法じゃないですよね)

ちなみに、同じ東アジア圏でも、中国や韓国のキーボードは英語配列と同じシンボルキーの配置のようです。日本語配列は、コンピュータの創世記、マルチバイト言語圏の雄として発展を遂げた日本独自の進化の結果と言ってもいいでしょうね。

UNIXツールを利用するのに最適 vi であっても nethack であっても、英語配列のキーボードで操作する事をベースとしてキーバインドが設定されています。日本語配列でも慣れてしまえば、あまり不自由がないような気もしますが、UNIX ツールを利用するには、やっぱり英語配列の方がしっくりくるでしょう。

英語配列のデメリット

配列自体にはデメリットはあまりないのですが、いざ利用するとなったら、「それ以外の点」でデメリットが生じます。

世知辛い ラップトップPCを購入しようとしても、おのずと購入できるものが限られてきます。海外製の安いふにゃふにゃキーボードなどは、むしろ英語配列の場合が多いんですが、日本メーカーのちゃんとしたラップトップPCを買おうとしても英語配列を選択できるものが少ないのも現状です。また、英語配列を選択できたとしても、日本語配列モデルより割高になってしまったり、と、ある程度の覚悟が必要だと思います。

これらは HHKB 以外の全ての英語配列キーボードに言えることです。HHKB のような「一度使ったら(色んな意味で)手放せない!」マニアックなキーボードを購入検討されている方であれば、いっその事、英語配列にしてしまうのもいいかと思います。事実、私も日々幸せになれました。

カーソルキーがない

カーソルキーがありません。ないので入力ができないかと言うとそうではなく、Fn キーと組み合わせることによって入力することができます。私は Vim では hjkl 移動、bash や emacs では C-p C-n C-f C-b 移動を普段から利用しているので、これらのツールでは特に困りませんが、多くの Windows アプリケーションではカーソルキー操作を前提としています。また、Windows でなくても GTK や Qt ベースのアプリケーションでも、最近では大抵がカーソルキーを装備した環境を対象にしています。カーソルキーを多用するアプリケーションでは間違いなく効率が低下するので、利用目的をしっかりと考えた上で購入する必要があります。
逆説的な話ですが、これまで使用していた Lite2 にはカーソルキーがあった為、Vim 上でもついついカーソルキーを使ってしまって、hjkl 移動はあまりやっていませんでした。カーソルキーが撤去される事によって、今ではホームポジションから手を動かす事なく自由に移動する事ができます。たった1週間、不便な思いをして慣らしていくと、その後は永遠に良い使い勝手を得られる、と前向きな解釈をしています。
また、タイプするキーが増えるものの、カーソルキーが独立していないことによって、ホームポジションから右手を数センチだけ右にシフトしてあげるだけで、入力ができるというメリットもあったりします。

タイプ感

他のユーザの方もおっしゃっているように、打鍵感はちょっと高めの音で「コトコト」と言った感じです。
Lite2 では、プラスチック音がカチャカチャと非常にうるさかったので、それに比べたら落ちついた印象になっていると思います。PFU の直販でしか購入ができない Type-S や、東プレの REALFORCE といった静音を売りにしたキーボードに比べれば、打鍵音は大きいと思いますが、タイプしていて心地いい感じがします。
キーの押し込み具合については、Lite2 がしっかり押し込まなければならないのに対し、Professional では、ちょっと押しただけでも入力が入ります。Lite2 のつもりで、考えごとをしてキーボードに手を乗せているだけでもどうかすると入力してしまいますが、通常の手の重さのみがかかった場合は、入力されることはありません。

メンテナンス性

公式サイトで専用ケースや、交換用のキートップが販売されているので、長く使用することができると思います。構造的にキーボードの内部にほこりが入り込むのですが、キーも簡単に取り外せます。(専用のキー外しもあります)

その他

Lite2 では USB ケーブルと本体を切り離すことができず、収納や持ち運び時にケーブルが邪魔になっていましたが、Professional2 ではケーブルを完全に取り外すことができます。端子形状も mini USB ですので、純正以外の USB ケーブルを代用することができ、自宅と職場のデスクに USB ケーブルをそれぞれ準備しておくと、本体だけを持ち運んで使うことができます。これは地味に嬉しい点です。

Delete キーと BackSpace キーは、背面にある DIP スイッチで切り替えることができます。標準だと「Delete キーと Fn キーを押しながらの BackSpace キー」という構成ですが、ここだけは抵抗があったので「BackSpace キーと Fn キーを押しながらの Delete キー」に変更しました。

また、買い替えなどで手放す場合、オークションサイトに出すと中古でもそこそこなお値段で流通しているようです。コンピュータ機器であるのに、価値が下がりにくいのは良い傾向ですよね。

総評

良いです。どうせ長く使うものですので、もう少し早く決心して購入しておけば良かったと思っています。
ちょうど冬のボーナスもあるし、自宅用に 2 台目買おうか検討しています。

Debian GNU/Linux Squeeze の MOTD 周りのメモ

12月になったし、MOTD(5) をクリスマス仕様にでもしようかなと /etc/motd を修正しても上手くいかなかったのでメモしておきます。

Debian GNU/Linux の Squeeze サーバに対して SSH 接続すると、デフォルトの状態で「ターミナルログインとしての MOTD」と「PAM の MOTD」が二重に出力されてウザかったので、後者を無効にしていたら、いつの間にか MOTD が全く表示されなくなっていました。デフォルト挙動が変更されたのかな。

まずおさらい

Debian GNU/Linux Squeeze の MOTD は次のようになっています。

/etc/motd -> /var/run/motd
/etc/motd.tail

で、起動時に実行される /etc/init.d/bootmisc.sh にて

[bash]
uname -snrvm > /var/run/motd
[ -f /etc/motd.tail ] && cat /etc/motd.tail >> /var/run/motd
[/bash]

と毎度毎度、motd の中身を書き換えやがります。(/etc/motd または /var/run/motd に記載していた内容は、起動時毎にぶち消されてしまいます。)

永続的なメッセージは /etc/motd.tail に記載せよ、という意図でしょうね。それに従って、表示させたい motd テキストを /etc/motd.tail に記載しておきます。

PAM の設定

本来の motd 表示がどのプロセスで表示されているのかは知りませんが、少なくとも PAM による認証時、pam_motd.so を有効にしておくと motd を吐いてくれます。ちなみに PAM とは、Pluggable authentication module の略で、UNIX 系 OS の共通認証システムの API です。アプリケーションの機能が、一部、特権を必要とする処理を行いたい場合、PAM の API を叩くだけで認証周りの面倒を見てくれる便利な API ですね。SSHD の認証システムにもこの PAM が利用されています。

/etc/pam.d/sshd の次の行のコメントアウトを外しました。

[bash]

Print the message of the day upon successful login.

session    optional     pam_motd.so # [1]
[/bash]

また

[bash]

Print the status of the user’s mailbox upon successful login.

session    optional     pam_mail.so standard noenv # [1]
[/bash]

の部分は、コメントが書いてあるとおり、ログイン成功時にメールボックスのメール件数を表示するモジュールです。

2013-07-23 追記

Wheezy にアップグレードした時からか、いつの間に MOTD が二重表示されるようになりました。
今回は、/etc/ssh/sshd_config の次の部分を変更し、sshd をリスタートすれば二重表示されなくなりました。

[bash]
– PrintMotd yes
+ PrintMotd no
[/bash]

Wheezy では、確認してみたところ bootmisc.sh の起動時毎に上書きする処理や motd.tail がなくなっていますね。ほとんど意味がない上に、ユーザを混乱させるだけだったから廃止されたかな。

軽量・高速な C コンパイラ「Tiny C Compiler」を使う

Tiny C Compiler (tcc)は軽量かつ高速なコンパイル、リンクが可能な C コンパイラです。コンパイルが非常に高速なので、抜き出した小さなコードでのテストや、あるプロジェクトのモジュール単位でのトライアンドエラー開発に向いていると言われています。

また、クイックリーなトライアンドエラーでのコーディングだけが目的であれば C インタプリタでも良いのですが、tcc はちゃんと機械語コードを吐くコンパイラですので、生成したバイナリはもちろんネイティブで実行可能です。tcc が生成したデバッグシンボルを gdb で利用することも可能です。

Debian GNU/Linux で利用する

Squeeze では標準リポジトリに入っていました。下記のコマンドをタイプするだけでインストールが完了します。

$ sudo aptitude install tcc

サンプルとして次のソースをビルドしてみたいと思います。

#include <stdio.h>
#include <ncurses.h>
int main(int c, char** a)
{
    initscr();
    printf("tcc is running!\n");
    endwin();
    return 0;
}

ちゃんとビルドできるかどうかの確認のため、ncurses ライブラリを使ってみています。

$ time tcc -lncurses -g -Wall -O3 -o tcctest test.c
real 0m0.018s
user 0m0.020s
sys 0m0.000s

ビルドすると一瞬でプロンプトに戻りました。ソースが短いとは言え早い。

$ ./tcctest

実行すると、もちろん正常に動作します。※ initscr() と endwin() の間に printf() が書かれているので、実行時には何も印字されないのが正しい動作です。

同じオプションで gcc を実行してみます。

$ time gcc -lncurses -g -Wall -O3 -o gcctest test.c
real 0m0.161s
user 0m0.128s
sys 0m0.040s

対象ソースが短いのであまり精度は良くありませんが、それでも10倍近くの時間がかかりました。 ldd で見てみたリンク状態は同じ、

$ diff -u <(nm gcctest) <(nm tcctest)

も同じです。アセンブリを見たくて tcc -S test.c してみると -S オプションはサポートしていないと怒られました。tcc 自体にもアセンブラは搭載されていますが、objdump などで見た方がいいみたいですね。 また、-g する事により、gdb に食わせることもできました。

$ echo 'int main(){ printf("hello\n"); }' | tcc -run -

で、動的にコードを入力、コンパイル、リンク、実行の一連の流れを実行します。

Windows で利用する

Windows にはパッケージ管理システムが機能していないので、自分で公式ページよりダウンロードし、解凍する必要があります。 コンパイラ・リンカ本体 tcc.exe、モジュール定義ファイル(DEFファイル)生成コマンド tiny_impdef.exe、「ar」コマンドに置き換わるライブラリ作成コマンド tiny_libmaker.exe が同梱されています。また、基本的なヘッダーやライブラリ、Win32 上でのサンプルコードまで同梱されています。 使い方は Debian 上での操作とほとんど変わりませんが、Windows 独自のコーディング方法がありますので、それについては example/ のソースコードと doc/tcc-win32.txt を参考すればいいと思います。

OpenCASCADE 6.5.4 を Microsoft Visual Studio でビルドする

2012年11月13日にリリースされた OpenCASCADE 6.5.4 を Microsoft Visual Studio でビルドする方法を忘れないうちにメモしておきます。

OpenCASCADE で単純に開発したいだけなら、バイナリパッケージのインストールだけで十分ですが、次のような場合は自前でビルドする必要があります。

  • Microsoft VIsual Studio 2008 以外のバージョンで開発したい。
  • OpenCASCADE 本体のソースコードを追いかけてデバッグしたい。

1. 必要なものを取得する

OpenCASCADE の公式ページDownload Center より、インストーラパッケージを取ってきます。ユーザ登録が必要ですので、登録後、公式ページにログインしてからダウンロードしてください。

ビルドに必要なパッケージは、OpenCASCADE654.exe だけです。また、開発にはクラスリファレンスも必要になってきますので、OCCTDocumentation654.exe も同時にダウンロードしておくべきでしょう。

OpenCASCADE654.tar.gz は、バイナリを含まないソースコードパッケージです。こちらの構成には、Visual Studio のソリューション・プロジェクトファイルの他に makefile も同梱されているので、非 Visual Studio 環境で開発する場合はこれを利用します。(今回は Visual Studio ですので割愛します。)

ちなみに、私の環境は Microsoft Windows 7 Professional 日本語版 32ビット、Microsoft Visual Studio 2008 SP1 です。

2. セットアップ

OpenCASCADE654.exe を実行すると、セットアップ・ウィザードが表示されます。インストールパスは、標準では「C:\OpenCASCADE6.5.4」となっていますが、任意の場所にインストールして構いません。(ただし、念の為にパス中に空白文字やワイド文字が入らない場所が無難でしょう。) 私の場合は、複数のバージョンの OpenCASCADE を使い分けているので「E:\opt\occ\654」にインストールすることにしました。この例でもこのパスを利用します。

次にインストール構成を設定します。「Custom installation」を選択するとインストールするものにチェックを入れて選択できるようになります。OpenCASCADE を利用した開発に最低限必要なのは、「Open CASCADE Technology」の「Binary Files」のみです。この Binary Files には、「Release」構成でビルドされた DLL ファイルの他、インクルードヘッダ、ライブラリファイルも含まれています。

今回は、自前で「Debug」構成のビルドをしたいので、バイナリに加えて「Source Files」も選択しておきます。「Documentation」は、先ほどダウンロードした OCCTDocumentation654.exe に入っているものとは違います。先ほどのファイルはクラスリファレンスですが、こちらのドキュメンテーションは、OpenCASCADE 全体について丁寧に解説してある PDF ファイルになります。有用な情報がたくさん入っているので、これもインストールした方がいいと思います。

また、サンプルも実際に動作する有用なコードがたくさん入っているので、自分の環境にあったものを入れておくと良いでしょう。

3. 簡単なディレクトリ構成の説明

インストールが終わると、指定した場所以下に全てのファイルが展開されています。ここでは「E:\opt\occ\654」を指定したので、そのフォルダを開くと以下のようなディレクトリが展開されていると思います。

 3rdparty ... OpenCASCADE が利用しているサードパーティ製ライブラリ群
 data     ... サンプルとして使える CAD データファイル
 doc      ... ドキュメンテーション
 ros      ... OpenCASCADE の本体が入ってるフォルダ
 samples  ... サンプルプロジェクト

ドキュメンテーションやサンプルをインストールしなければ、いくつか存在しないフォルダがあるかもしれません。

ソースコードやプロジェクトファイルが入っているのは、ros フォルダの下になります。この ros フォルダが OpenCASCADE を用いた開発の基点となるフォルダですので、以後 CASROOT と記載します。(OpenCASCADE では、この ros フォルダへのフルパスを環境変数 CASROOT として定義して参照しています。OpenCASCADE 関連のドキュメントで CASROOT という表現が出てきたら ros フォルダのことだと思ってください。)
CASROOT の内容は次のとおり。

 adm        ... プロジェクトファイルが入っているフォルダ
 drv        ... クラスの定義ファイルが入っているフォルダ
 inc        ... インクルード・ヘッダが入っているフォルダ
 src        ... ソースファイルが入っているフォルダ
 win32      ... win32 向けのバイナリとライブラリファイルが入ってるフォルダ
 custom.bat ... 開発者の環境に合わせて環境変数などの定義を変更するためのスクリプト
 draw.bat   ... Draw Test Harness を起動するためのスクリプト
 env.bat    ... OpenCASCADE が必要とする環境変数を定義するスクリプト
 msvc.bat   ... ビルドに必要な環境変数を設定し、Visual Studio を起動するためのスクリプト

それぞれのバッチファイルは、内部で env.bat を呼び出しています。OpenCASCADE のサンプルを実行したり、ビルドに必要な環境変数は env.bat や msvc.bat をテキストエディタで開いてみると分かると思います。

4. ビルド

以前のバージョンでは、日本語 Windows 環境では修正をしないとビルドが通りませんでしたが、今回の 6.5.4 は、そのままの状態でビルドが通るようになっています。

前述のとおり、OpenCASCADE をビルドする為に様々な環境変数を利用しています。そのため、adm フォルダ以下にあるソリューションファイル(.sln)またはプロジェクトファイル(.vcproj)をそのまま開いてもビルドする事ができません。

そこで、環境変数を設定して Visual Studio を起動してくれるスクリプト msvc.bat を実行します。

まず、コマンドプロンプトを起動して CASROOT に移動します。

> cd /d E:\opt\occ\654\ros

次に、msvc.bat を引数付きで実行します。

> msvc.bat vc9 win32 Debug

引数の意味は始めから、Visual Studio のバージョン、ビルド対象のプラットフォーム、ビルドの構成の順に指定します。私の環境は Visual Studio 2008 ですので、第1引数に vc9 を指定しています。vc8 が 2005、vc10 が 2010 になります。紛らわしいので注意が必要です。
第2引数には win32 または win64 が指定できます。第3引数は、「d」もしくは「Debug」を指定しないと Release 構成(デバッグシンボルとかがついていないバイナリを生成)になります。
Release 構成でビルドしたバイナリは、セットアップの手順で「Binary Files」を選択しておけば、自前でビルドしなくても最初からインストールされています。今回はデバッグシンボル付きの Debug 構成バージョンが欲しいので、Debug 構成でビルドすることとします。

※以前のバージョンでは、Visual Studio のインストールパス(デフォルトで C:\Program Files 以下)に空白文字が入っていると、msvc.bat から起動できないという致命的なバグがありましたが、本バージョンでは修正されているようです。

Visual Studio が起動したら、OCCT.sln というソリューションファイルが開かれていることを確認します。このソリューションファイルの実体は %CASROOT%\adm\msvc\vc9\OCCT.sln です。このソリューションには、OpenCASCADEの全67プロジェクトが属していますので、あとは「ソリューションをビルド」するだけでOKです。フルビルドには結構時間がかかりますので、お茶でも呑みながらまったり待ちます。

5. 確認

エラーが発生せずに、すべてのビルドが完了したら成功です。ビルドの成果物は、%CASROOT%\win32\vc9 以下に出力されます。

 bin  ... Release ビルドのバイナリファイル(*.dll;*.exe)
 bind ... Debug ビルドのバイナリファイル(*.dll;*.exe)とデバッグシンボル(*.pdb)
 lib  ... Release ビルドのライブラリファイル(*.lib)
 libd ... Debug ビルドのライブラリファイル(*.lib)
 objd ... Debug ビルドの中間オブジェクトファイル、不要なので削除可。

bind や libd など、末尾に d がついているフォルダは Debug ビルドを意味しています。

OpenCASCADE を用いたアプリケーションを開発するには、インクルードパスに %CASROOT%\inc を、ライブラリパスに %CASROOT%\win32\vc9\lib または libd を追加すればOKです。また、そのアプリケーションを実行するには、%CASROOT%\win32\vc9\bin または bind に PATH を通します。さらに OpenCASCADE が利用しているサードパーティ製のライブラリ(%CASROOT%..\3rdparty 以下のフォルダ)のバイナリファイルがあるフォルダにも PATH を通す必要があります。

最新 Draw Test Harness の配布

2017-03-27 追記

この記事で紹介している Open CASCADE Technology Draw Test Harness の最新版は、公式配布のパッケージ内にあります。

Download Open CASCADE Technology 7.1.0 | OPEN CASCADE

上記の URL から入手することができます。(無料です。ダウンロードには登録が必要)

詳しい使い方は要望があれば記事にしたいと思います。

(追記終わり)


OpenCASCADE 最新版 6.5.3 の Draw Test Harness (対話的なコマンド実行環境) の Windows 用バイナリパッケージをアップロードしました。Open CASCADE Technology Public License に基づいて配布しています。

ダウンロード: dth653.zip (約21MB)

DTH を実行する為に必要な依存関係も一式入っているので、ダウンロード後、解凍するだけで実行できると思います。OpenCASCADE 本体が依存して利用しているライブラリは次のとおりです。配布ライセンスはそれぞれのものになります。

Tcl/Tk, FreeImage, FTGL, GL2PS, TBB, MSVCランタイム(リリース)

テスト環境とは言うものの、これだけでもかなりの事ができます。 例えば、IGESモデルを三次元空間上に読み込み、任意のBスプライン曲面で切り取った面積を求めたり、ソリッドモデル同士のブーリアン演算を行なったり、曲面に他のオブジェクトを投影したり、オフセットを取ったり・・・etc。 詳しい使い方は、ヘルプコマンドを実行するか、同梱の thug.pdf をご覧ください。

FastCGI+lighttpd で C プログラムを動かす

FastCGI(fcgi)とは、通常のCGIのようにリクエストが発生する度に起動、実行される仕組みではなく、一度起動したら起動しっぱなしでリクエストを処理するタイプのCGIです。常にメモリの上に乗っているので、メモリを食いますが、大量のリクエストをオーバーヘッドなしで処理できる利点があります。

FastCGIでバイナリプログラムを実行するケースのパフォーマンスはなかなか良いらしいので、我が家のlighttpdサーバにも導入してみることにしました。

久しぶりに lighttpd.conf を覗いてみると、モジュール読み込み部分に既に mod_fastcgi が登録されていました。(そう言えば、以前 trac を動かしてみた際に FastCGI を使ってたっけ…)

server.modules = (
"mod_access", "mod_alias",
"mod_accesslog", "mod_compress",
"mod_redirect",
"mod_expire", "mod_fastcgi"
(略)
)

こんな感じです。次に、FastCGIの設定を行います。

fastcgi.server = (
  "/fcgi" => (
    "fcgi" => (
      "socket" => "/var/run/lighttpd/fcgi.socket",
      "check-local" => "disable",
      "bin-path" => "/var/www/fcgi-bin/test.fcgi",
      "min-procs" => 1,
      "max-procs" => 2
    )
  )
)

bin-path には、これから準備するバイナリのパスを指定しています。
我が家では、上の fcgi の設定に加えて、trac の設定もしてあります。

次にバイナリの用意です。FastCGI用のライブラリを持ってきます。

[bash]
$ sudo aptitude install libfcgi-dev
[/bash]

bin-path で指定した位置にバイナリを作ります。

[bash]
$ cd /var/www
$ sudo mkdir fcgi-bin
$ cd fcgi-bin
$ sudo vim fcgi.c
[/bash]

fcgi.c の内容は次のとおりです。

[cpp]

include <fcgi_stdio.h>

int main(int c, char** argv)
{
int cnt = 0;
while (FCGI_Accept() >= 0)
printf("Content-type: text/html\n\ncount=%d\n", ++cnt);
return 0;
}
[/cpp]

fcgi_stdio.h をインクルードすると、printf() などの標準ライブラリ関数が FastCGI のものに置き換えられ、FCGI_Accept() などの関数が使えるようになります。

このコードをビルドします。-lfcgi オプションで FastCGI へのリンクもお忘れなく。

[bash]
$ sudo gcc -o test.fcgi test.c -lfcgi -Wall -O3
$ sudo chmod a+rx test.fcgi
[/bash]

念の為、全ての権限に rx を付与しています。
最後に設定を反映させる為、lighttpd を再起動します。

[bash]
$ sudo /etc/init.d/lighttpd restart
[/bash]

lighttpd.conf の fastcgi.server で設定したパスにアクセスしてみます。

[bash]
$ curl http://127.0.0.1/fcgi
count=1
[/bash]

何度もアクセスしてみて、count の値が増えつづければプロセスが常駐していることが分かります。

[bash]
$ curl http://127.0.0.1/fcgi
count=2
$ curl http://127.0.0.1/fcgi
count=3
$ curl http://127.0.0.1/fcgi
count=4
[/bash]

ライブラリを作成、使用する(Windows編)

先日、覚え書きとして「ライブラリを作成、使用する」を書きましたが、同じ事を Microsoft Windows と Microsoft Visual Studio で行うためのメモです。

環境は Microsoft Windows 7 Professional 日本語版 32bit / Microsoft Visual Studio 2010 Professional です。

Linux では共有ライブラリと静的ライブラリでしたが、Windows の世界ではダイナミックライブラリ(.dll)と 共通オブジェクトファイル COFF(.lib)があるようです。後者は、Microsoft の文書中で「COFF形式の標準ライブラリ」とも表記されていますが、C 標準ライブラリとは無関係で、Linux で言う静的ライブラリの事を指すようです。

                       | Linux              | Windows
標準コンパイラ         | gcc                | cl.exe
オブジェクトファイル   | .o                 | .obj
静的ライブラリ         | .a                 | .lib
共有ライブラリ         | .so                | .dll
インポートライブラリ   |                    | .lib
エクスポートライブラリ |                    | .exp
エクスポート定義       | GCC version script | .def
ライブラリ検索パス     | 可変               | 強制
バージョン管理         | 環境変数、リンク   | Side by Side

DLL だけでも、レギュラー DLL、MFC に静的リンクしたレギュラー DLL、MFC に動的にリンクしたレギュラーDLL、MFC 拡張 DLL、COM をサポートした DLL(OLE/ActiveX/COM+)、Microsoft .NET Framework 向けのマネージド DLL など、様々な種類や実装方法があるようですが、最も純粋なレギュラーDLLをベースに書きたいと思います。

作業に取り掛かる前に、開発環境に必要な環境変数を定義する必要があります。Visual Studio をインストールすると、環境変数「VS100COMNTOOLS」が定義されます。これは名前のとおり、Visual Studio 2010 の共通ツールへのパスが定義されています。標準では「C:\Program Files\Microsoft Visual Studio 10.0\Common7\Tools」というパスになっているはずです。Visual Studio 2008 や 2005 でも VS**COMNTOOLS という環境変数が定義されるはずですので、読み替えればOKだと思います。

コマンドプロンプトを立ち上げ、次のようにタイプします。

> "%VS100COMNTOOLS%\vsvars32.bat"
 Setting environment for using Microsoft Visual Studio 2010 x86 tools.

これで開発に必要なツールに PATH が通り、開発ができるようになります。(なお、%VS100COMNTOOLS% には標準で空白文字が含まれているので、上記のようにダブルクォートで囲ってあげないと、変数展開後にパスを見失なってしまうので注意が必要です。vsvars32.bat で定義された環境変数の寿命は、コマンドプロンプトのセッション内ですので、コマンドプロンプトを一度閉じてしまったら、再度、上記のコマンドを実行する必要があります。)

前回と同様、サンプルとして次の func.c を準備します。

[cpp]
int add(int a, int b)
{
return a+b;
}
[/cpp]

二つの整数値を足して、その値を返すだけの簡単な関数 add() を定義しています。

オブジェクトファイルの静的リンク

前回と同様、Microsoft の C コンパイラ cl.exe も -c オプション(コンパイルのみ)をサポートしています。 Microsoft 流儀で言うと /c オプションとなりますが、このオプションはハイフン(-)でもスラッシュ(/)でも通るようです。実際に func.c をビルドすると、静的ライブラリ func.obj が作成できます。gcc では func.o ですが、拡張子が DOS 風に3文字となっています。

> cl -c func.c
 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
 Copyright (C) Microsoft Corporation.  All rights reserved.

 func.c

Microsoft の著作権情報は /nologo オプションで非表示にすることができます。コマンドラインが長くなりますが… :(

呼び出し元から静的リンクするには、次のように一緒にビルドするだけです。ここはほとんど gcc と変わりませんね。

> cl /out:app.exe app.c func.obj
 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
 Copyright (C) Microsoft Corporation.  All rights reserved.
app.c
 Microsoft (R) Incremental Linker Version 10.00.30319.01
 Copyright (C) Microsoft Corporation.  All rights reserved.
/out:app.exe
 app.obj
 func.obj
> .\app.exe
 a+b=8

gcc の -o オプションは cl だと /out: になります。省略すると、最初に指定されたファイル名に拡張子 exe をつけたものを吐いてくれますが、ビルドするファイルが多い場合や処理を自動化した場合、予期せぬファイル名を自動的につけて出力する危険性があるので、/out: オプションはなるべく省略しない方がいいと思います。

ちなみに、コマンドプロンプトから実行ファイルの指定を確実にするには、

> .\app.exe

と相対パスで実行する方が無難でしょう。

ダイナミックリンクライブラリを動的リンク

共有ライブラリのようなものとして、ダイナミックリンクライブラリ(DLL)という実装があります。これは、前回の Linux と gcc をやった時のようにコンパイルオプション一つ変更するだけで完成する訳ではなく、専用の記述やファイルを準備しなければなりません。

func.c を次のように修正します。

[cpp]
__declspec(dllexport)
int add(int a, int b)
{
return a+b;
}
[/cpp]

__declspec(dllexport) が関数宣言の前につきました。これは、ライブラリが公開する関数を制御する為に用いられるものらしいです。前回に比べればちょっと面倒くさいですが、そういうものらしいです。

これをビルドします。

>cl /LD func.c
 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
 Copyright (C) Microsoft Corporation.  All rights reserved.

 func.c

 Microsoft (R) Incremental Linker Version 10.00.30319.01
 Copyright (C) Microsoft Corporation.  All rights reserved.

 /out:func.dll
 /dll
 /implib:func.lib

 func.obj

 ライブラリ func.lib とオブジェクト func.exp を作成中

オプションは /LD です。大文字・小文字を判別しますので注意してください。上手くビルドされると、4つのファイルが生成されます。

 func.dll ... DLL ファイル。
 func.exp ... エクスポートライブラリ。循環参照の解決に用いられるらしい。
 func.lib ... インポートライブラリ。外部から DLL を参照する際に用いられる。
 func.obj ... オブジェクトファイル。

これで DLL ファイルが完成しました。

次は、DEF ファイルを用いて作成してみます。DEF ファイルを用いると、オリジナルのソースコードをライブラリ化の為に変更しなくていいので、func.c を最初のサンプルのように戻します。

[cpp]
int add(int a, int b)
{
return a+b;
}
[/cpp]

そして func.def という新しいファイルを作成し、下記のように入力します。

LIBRARY    func
EXPORTS
     add @1

add 関数を序数1で「エクスポート(つまり公開)」する、という意味らしいです。

DEF ファイルを用いてビルドするには次のコマンドを実行します。

>cl /LD /DEF:func.def func.c
 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
 Copyright (C) Microsoft Corporation.  All rights reserved.

 func.c

 Microsoft (R) Incremental Linker Version 10.00.30319.01
 Copyright (C) Microsoft Corporation.  All rights reserved.

 /out:func.dll
 /dll
 /implib:func.lib

 func.obj

DEF ファイルが、gcc でいうバージョンスクリプト的なものなら、なんとなく理解できる仕様です。ソースコードと DEF ファイルの管理が煩雑にならないか注意する必要がありますね。

さて、生成された DLL ファイルを利用するには、app.c も改変します。

[cpp]

include <stdio.h>

__declspec(dllimport) int add(int, int);
int main()
{
printf("a+b=%d\n", add(3, 5));
return 0;
}
[/cpp]

dllimport がついた関数宣言を追加しました。ただし、前回と同様に必須ではなく、MSDNでの解説曰く

__declspec(dllimport) を使用しなくてもコードは正しくコンパイルされますが、使用した方がコードがより洗練されます。__declspec(dllimport) を使うと、ある関数が DLL 内にあるかないかをコンパイラが判断できるようになり、その結果、コードでは通常 DLL の境界を越える関数呼び出し内に存在する間接操作のレベルをスキップできます。ただし、DLL 内に変数をインポートするには、__declspec(dllimport) を必ず使用する必要があります。

との事です。

さらにビルド時には、インポートライブラリを渡してやらなければなりません。func.lib を指定していますが、この場合は共通オブジェクトファイルとは全く別のものなので注意が必要です。

> cl app.c func.lib
 Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
 Copyright (C) Microsoft Corporation.  All rights reserved.

 app.c

 Microsoft (R) Incremental Linker Version 10.00.30319.01
 Copyright (C) Microsoft Corporation.  All rights reserved.

 /out:app.exe
 app.obj
 func.lib

 E:\tmp>app.exe
 a+b=8

これで実行バイナリ app.exe が生成されます。
ちなみに、インポートライブラリを使わずに DEF ファイルを用いて「インポート」する方法もあるらしいです。

ダイナミックリンクライブラリを動的ロード

ライブラリを作成、使用する

Linux 環境で gcc を用いて共有ライブラリを作成する際の基本的なことをメモしておきます。 g++ でも手順は同じです。

サンプルとして次の func.c を準備します。

int add(int a, int b)
{
    return a+b;
}

二つの整数値を足して、その値を返すだけの簡単な関数 add() を定義しています。

オブジェクトファイルの静的リンク

func.c-c (コンパイルのみ)でビルドすると、オブジェクトファイル func.o が作成できます。

$ gcc -c func.c

呼び出し元から静的リンクするには、次のように一緒にビルドするだけです。

$ gcc -o app app.c func.o
$ ./app
a+b=8

複数のオブジェクトファイルをまとめて、静的ライブラリにする

a.cb.cc.c のそれぞれがあった場合、アーカイブコマンド ar によってライブラリをまとめることができます。

$ gcc -c a.c b.c c.c
$ ar r libabc.a a.o b.o c.o

まとめられた libabc.a はただのアーカイブファイルですので、ranlib でライブラリ内のインデックスを構築してあげます。

$ ranlib libabc.a

libabc.a を使ったソースのビルドはこんな具合です。

$ gcc app.c libabc.a

共有ライブラリの動的リンク

func.c を共有ライブラリ libfunc.so としてビルドします。

$ gcc -shared -o libfunc.so func.c

-shared オプションをつけるだけです。このライブラリを使うには、次のような app.c を準備して

#include <stdio.h>
int main()
{
    printf("a+b=%d\n", add(3, 5));
    return 0;
}

まとめてビルドします。ヘッダファイルも不要です。

$ gcc -o app app.c libfunc.so
$ LD_LIBRARY_PATH=. ./app
a+b=8

ライブラリパスをカレントに指定して実行すると、ちゃんと libfunc.so の機能が使えていることが分かります。 簡単ですねー。

共有ライブラリの動的ロード

dlopen(3) を用います。ライブラリ側に特殊なコーディングは必要ありません。上記のように共有ライブラリとしてビルドすればOKです。

#include <stdio.h>
#include <dlfcn.h>
int main(void)
{
    void *handle
    = dlopen("./libfunc.so", RTLD_LAZY);         // ライブラリを開く
    int (*add)(int, int) = dlsym(handle, "add"); // 関数ポインタを取ってくる
    printf("a+b=%d\n", (*add)(3, 5));            // 関数を叩く
    dlclose(handle);                             // ライブラリを閉じる
}

このコードは次のようにビルドします。

$ gcc -rdynamic -ldl -o app2 app2.c
$ ./app2
a+b=8

-ldl は動的ロードライブラリへのリンクです。

参考リンク

その他、留意点などは次のページを参考にしてください。

MewとSylpheedでnetnewsを読み書きする

プライベートなニュースサーバを稼動してみただけで安心してしまって、各種クライアントの設定は一度だけ行なったものの放置していたので、覚書として書いておくことにしました。

Mewでnetnewsを読み書きする

普段は、コンソール端末経由でどこからでも利用ができる Mew on Emacs でメールをしています。キーバインドを覚えるまでは敷居が高そうだったですが、覚えた後の効率は鬼の如しです。

まず、~/.mew.el にnetnewsの設定を記載します。

[lisp]
(setq mew-nntp-server “news.example.com”)
(setq mew-nntp-user nil)
(setq mew-nntp-header-only t)
(setq mew-nntp-newsgroup “-groupname”)
[/lisp]

私の場合、上のように設定したらOKでした。自宅LAN内からのアクセスのみを許可しているので、ユーザ認証は設けていません。(出先からアクセスしたい場合は、SSHトンネル経由でLAN接続しています)
公式のリファレンスや古いML記事などにmewでnetnewsをする為の設定はちらほら書いてありましたが、絶対数が少ない上にニッチな設定方法が多かった為、今の自分の環境に適した設定方法を見つけるのには苦労しました。私が見つけた中で、一番最近のmew+netnewsの記事でも既に5年前のものでした。

  1. Mew を起動します。
  2. g -groupname をタイプし、グループディレクトリを移動します。もし、「Type ‘C-uZ’ to collect newsgroups」と怒られてしまったら、C-u Z をタイプしてローカルのグループディレクトリを作成してください。
  3. メッセージの取得は s all です。 mew-nntp-header-only を t にしてますので、メッセージヘッダのみをダウンロードします。
  4. 本文をダウンロードするには、サマリでメッセージにカーソルを合わせた状態で I をタイプします。

あとは、通常と同じオペレーションで、a でリプライしたり w で投稿したりできます。

Sylpheedでnetnewsを読み書きする

マルチプラットフォームであり、メールデータの内部保持を mbox ライクなプレーンな形式で持っており、使い勝手もなかなか良いので Windows では Sylpheed を利用しています。

  1. メニューの「設定」から「アカウントの編集」を選択します。
  2. 「追加」ボタンを押し、「新規アカウントの設定」ダイアログを表示します。
  3. 「基本」タブの「サーバ情報」フィールド内にある「プロトコル」を「ニュース(NNTP)」に設定し、サーバの設定を入力します。
  4. アカウントの追加が完了すると、メイン画面の「フォルダ」に新規に追加したニュースのアカウントが追加されます。
  5. そのフォルダを右クリックして、「ニュースグループを購読」をクリックします。
  6. 目的のニュースグループを追加すると、フォルダツリーにサブディレクトリとしてグループが追加されます。
  7. グループフォルダを右クリックして、「ダウンロード」すると新着のメッセージを取得することができます。

上記の手順で設定を終えると、あとは通常のメールと同じようなオペレーションで投稿やリプライができます。

Mozilla Thunderbird で netnews を読み書きする

Bash, Perl, Ruby, Pythonで正規表現置換

前回のC++/boost.NETに加え、各種インタプリタ言語でも同じ動作をするスクリプトを書いてみました。

シェルスクリプト(bash)

シェルスクリプトはそもそもグルー言語ですので、他のコマンドを呼び出して処理をすることが一般的です。下のサンプルではsedコマンドで置換処理をしています。bashだと正規表現マッチングは可能なので、ガリガリとスクリプトを書けばsedを使わずに実現できるかもしれません。

[bash]

!/usr/bin/env bash

[ $# -ne 2 ] && exit 1
cat | sed -e "s/$1/$2/g"
exit 0
[/bash]

リプレイスメント置換文字は、sedの書式になります。グループ指示子はダラー($)ではなくバックスラッシュ(\)を用いています。

Perl

Perlの場合、正規表現による文字列操作は、関数でもクラスでもなく構文として組み込まれています。その為、コマンドライン引数から渡された「$1」のようなリプレイスメントの展開方法に、若干の工夫が必要です。

[perl]

!/usr/bin/env perl

exit 1 if ($#ARGV != 1);
for (<stdin>) {
eval "s/$ARGV[0]/$ARGV[1]/g" && print;

s/$ARGV[0]/$ARGV[1]/gee && print; # これではダメ

}
exit 0;
[/perl]

リプレイスメントが格納されている$ARGV[1]は展開されると、例えば「$1$2」という文字列になります。この展開後の文字列をリプレイスメントとして正規表現置換の処理に投げたいのですが、正規表現置換処理を行なった後に変数展開されるようでリプレイスメントのグループ指示子としての$nが正規表現置換処理に伝わりません。そこで、正規表現置換処理を行う前に$ARGV[1]を展開させるべく、eval関数に投げています。これにより、正規表現置換処理が評価される時点でリプレイスメントは「$ARGV[1]」ではなく「$1$2」という文字列として解釈され、指示子が正しく伝わるようです。
検索パターンに加え、リプレイスメント文字列も変数で持つという処理は多々あるはずなので、もっとスマートな方法が準備されているのかもしれませんが、性質さえ知っていれば公式を知らずとも期待する処理が可能である、まさに”TMTOWTDI / There’s More Than One Way To Do It(やり方はひとつじゃない)”という設計思想を持つPerlらしい実装です。理に適った挙動は見ていて気持ちがいいです。

Python

Pythonでは、シェルスクリプトやPerlと違って正規表現機能はクラス(re)として提供されています。下のサンプルではreの静的関数を叩いていますが、プリコンパイルしたオブジェクトとしても利用できたと思います。

[python]

!/usr/bin/env python

import sys
import re
v = sys.argv
if len(v) != 3: quit(1)
for line in sys.stdin:
print re.sub(v[1], v[2], line),
quit(0)
[/python]

リプレイスメントのグループ指示子は、sedと同じくバックスラッシュです。Cやシェルライクなエスケープ文字という捉え方をするなら、バックスラッシュがしっくり来ますね。(といっても、ダラーの場合でも同じく「シェルライク」ですけど :D )

ちなみに、len() がオブジェクトのメソッドではなく独立した関数になっているのは、評価対象(上の例の場合は v )が null オブジェクトでも、null チェックなしで利用できるようにする為だとどこかで読みました。Python の場合は空文字列(len=0)は null オブジェクトになるんですね。

Ruby

Rubyの場合は、シェルスクリプトを除いた他のどの言語よりもマニアックな感じがしますが、よく見てみると一番「ナチュラル」に理解し易い構文です。これだけの構文で比較するのはアレかもしれませんが、これだけの構文だけでここまで革新的な要素をたくさん見てとれるのは、rubyだからこそと言った感じでしょうか。ファイルディスクリプタまでオブジェクトであり、イテレータをせおっているあたり、カワイイです。

[ruby]

!/usr/bin/env ruby

if ARGV.size != 2 then exit 1 end
STDIN.each_line do |line|
puts line.gsub(/#{ARGV[0]}/, ARGV[1])
end
exit 0
[/ruby]

Rubyの何十倍もPythonのコードは書いていますが、Pythonは「VB.NETよりも使いものになる、綺麗なVB.NET的な何か」というイメージが拭えません。いいところ、悪いところの両方を知った上で使いこなせていけたらいいなあ、と思いました。

See also