情報処理技術者試験テクニカルエンジニア(ネットワーク)合格した!!

最近は仕事でサーバ構築*1をしてたから、いい機会かなと思って受験した*2ら受かった!!
僕はプログラマネットワーク管理者じゃないけど、いい勉強になった。TCP とか、ストリーム抽象だから便利、くらいの認識しかなかったけど、安定性と効率のためにすごく工夫されてて面白かった。一番印象に残ったのは、VLAN とL3スイッチ。世の中にはこんな便利な技術があるんだねぇ。RIP とかも、この先使うことはなさそうだけど興味深かったよ。
ネットワークプログラミングをいつもとは違う視点で眺められてすごくいい経験になったので、プログラマの方には結構お薦めです。

*1:部内のソースコード管理サーバだけどね。

*2:もちろん、ちゃんと勉強もしたよ。

プロプロセッサのマクロ展開と ## 演算子 (2)

前回を書いてからすっかり経ってしまったが、C/C++ プリプロセッサのマクロ展開と ## 演算子の話。
まずは、__TEXT がどう解釈されるのかを見てみる:

#define __TEXT(quote) L ## quote

C++ 標準*1 にはこうある:

『16.3 Macro replacement』の「9」
A preprocessing directive of the form
# define identifier lparen identifier-listopt ) replacement-list new-line
defines a function-like macro with parameters, similar syntactically to a function call. The parameters are specified by the optional list of identifiers, whose scope extends from their declaration in the identifier list until the new-line character that terminates the #define preprocessing directive. Each subsequent instance of the function-like macro name followed by a ( as the next preprocessing token introduces the sequence of preprocessing tokens that is replaced by the replacement list in the definition (an invocation of the macro). The replaced sequence of preprocessing tokens is terminated by the matching ) preprocessing token, skipping intervening matched pairs of left and right parenthesis preprocessing tokens. Within the sequence of preprocessing tokens making up an invocation of a function-like macro, new-line is considered a normal white-space character.
なので、最初の展開はこうなる:
展開前

__TEXT(HELLO_WORLD)

展開後

L ## quote

次にくるのは、この規定:
『16.3.1 Argument substitution』
After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. A parameter in the replacement list, unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below), is replaced by the corresponding argument after all macros contained therein have been expanded. Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the translation unit; no other preprocessing tokens are available.
というわけで、通常はここで quoteHELLO_WORLD に置換されるはずが、「preceded by a ##」なので quotequote のままにされる。
で、その次はこれ:
『16.3.3 The ## operator』の「2」
If, in the replacement list, a parameter is immediately preceded or followed by a ## preprocessing token, the parameter is replaced by the corresponding argument’s preprocessing token sequence.
というわけで、以下のように quoteHELLO_WORLD に置換される:
置換前

L ## quote

置換後

L ## HELLO_WORLD

あれっ!? さっきは「preceded by a ##」だから quoteHELLO_WORLD に置換されないって言ったくせに、結局 HELLO_WORLD になっちゃったよ?? いやいや、同じではない。さっきは「replaced by the corresponding argument after all macros contained therein have been expanded」だったが、今度は「replaced by the corresponding argument’s preprocessing token sequence」としか書かれていない。そう、ここでは、マクロ HELLO_WORLD はマクロ展開を受けずにプリプロセッサトークンのままになるのだ。
そして、最後にこれがくる:
『16.3.3 The ## operator』の「3」
For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace, each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and the preceding preprocessing token is concatenated with the following preprocessing token. If the result is not a valid preprocessing token, the behavior is undefined. The resulting token is available for further macro replacement. The order of evaluation of ## operators is unspecified.
つまりは、こういうことだ:
## の処理前

L ## HELLO_WORLD

## の処理後

LHELLO_WORLD

そして、この LHELLO_WORL こそが、このエラーメッセージの元凶だ:

(11) : error C2065: 'LHELLO_WORLD' : 定義されていない識別子です。

では、最後に復習を。ちゃんとコンパイルされる方のマクロ呼び出しである TEXT(HELLO_WORLD) は、こんな感じで展開されていく:

  1. TEXT(HELLO_WORLD)
    


  2. __TEXT(quote)
    


  3. __TEXT(HELLO_WORLD)
    


  4. __TEXT("Hello, world.")
    


  5. L ## quote
    


  6. L ## "Hello, world."
    


  7. L"Hello, world."
    


プリプロセッサのテクニック本なんかに出てくる「引数として渡されたマクロ呼び出しを強制的に評価するためにマクロ定義は2重にしておく」みたいな記述は、こういことなのである。

*1:ISO/IEC 14882:2003(E) 『Programming languages − C++

プロプロセッサのマクロ展開と ## 演算子 (1)

Windows API には TCHAR 型というものがある((MSDN ライブラリでは TCHARunsigned chartypedef だと解説されているが、Visual C++ 2008 Express Edition に付いてきた WinNT.h では chartypedef になっていた。)):

#ifdef UNICODE
    typedef wchar_t TCHAR;
#else
    typedef char TCHAR;
#endif

wchar_t 型ではないのでワイド文字としては扱えず、char でもないので標準 C ライブラリなどの各種 API にも渡せないという扱いづらいやつだが、コードページから Unicode への過渡期には大きな役割を果たしてくれた型である。
さて、この TCHAR 型には TEXT() マクロという相方がいて、“TCHAR リテラル”とでも呼ぶべきものを書けるようになっている。すなわち、TEXT("some string") と書くと、マクロ UNICODEdefine されているかに応じて以下のいずれかに展開される:

UNICODE が定義されている
L"some string"
UNICODE が定義されていない
"some string"
面白いマクロなので定義を見てみよう((WinNT.h で define されている。必要なところだけ抜き出してきて整形してある。)):

#ifdef UNICODE
#   define __TEXT(quote) L ## quote
#else
#   define __TEXT(quote) quote
#endif

#define TEXT(quote) __TEXT(quote)

キーになるのは、## という演算子だ。これは、マクロの置換テキストの中でのみ利用可能な演算子で、マクロの実引数を字句レベルで連結するという離れ業をやってのける。これにより、TEXT("some string") は、__TEXT("some string") から L ## "some string" を経て L"some string" に展開される。
ところで、ここに1つの疑問が湧く。なんで、__TEXT() マクロを経由しないといけないのだろう? シンプルに「#define TEXT(quote) L ## quote」ではダメなのだろうか?
結論から言うと、このシンプルな解はダメだ。以下の2つをそれぞれコンパイルしてみれば分かる:

コンパイル可能

#include <windows.h>
#include <tchar.h>

int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPTSTR    lpCmdLine,
                        int       nCmdShow )
{
    MessageBox(NULL, __TEXT("Hello, world."), TEXT("hello"), MB_OK);
    return 0;
}

コンパイル不能

#include <windows.h>
#include <tchar.h>

#define HELLO_WORLD "Hello, world."

int APIENTRY _tWinMain( HINSTANCE hInstance,
                        HINSTANCE hPrevInstance,
                        LPTSTR    lpCmdLine,
                        int       nCmdShow )
{
    MessageBox(NULL, __TEXT(HELLO_WORLD), TEXT("hello"), MB_OK);
    return 0;
}

両者の違いは、__TEXT() マクロに渡す "Hello, world." という文字列を直接書くか HELLO_WORLD というマクロ経由で渡すかということだけだ。ただそれだけの違いで、コンパイルができたりできなかったりする。そして、__TEXT() ではなく TEXT() を使えば、HELLO_WORLD マクロ経由でもコンパイルできるようになるのだ。
では、これを切り口にしてプリプロセッサのマクロ展開と ## 演算子の面白い挙動を見ていこう、……と思ったが、長くなってしまったので今日はこの辺りまでで。ちなみに、全てを物語るのは、コンパイラからのこのメッセージだ:

(11) : error C2065: 'LHELLO_WORLD' : 定義されていない識別子です。

C:\ の直下にはインストールしない方がいいと思う

最近はあまり見ないが、インストール先を C:\ の直下にしようとするインストーラやインストールインストラクションがある。
C:\ の直下がゴチャゴチャするのが何となく嫌なので、この手のソフトウェアは C:\opt というディレクトリの下にインストールしている。そう、今まではただ「何となく」こうしていたのだが、先日、「実はこうしないとヤバいんじゃないの?」と思うところがあったので、日記にでも書いてみることにした。
結論から言うと、こういうことだ:

  • C:\ の直下にインストールしちゃうと、そこのバイナリとか誰でも書き換えられちゃいませんか?

Windowsパーミッション設定をよく理解できていないのかもしれないが、つい先日 32ビット版の Windows Vista Business をクリーンインストールしたばかりのこのマシン、Authenticated users グループに C:\ の直下への書き込み権*1がある。そんな設定をわざわざやった覚えはないので、多分デフォルトなんだろう。
で、Windows では、ディレクトリの中にファイルやサブディレクトリを作ると基本的には親ディレクトリの ACL が引き継がれる。だから、C:\foobar みたいなところにアプリケーションをインストールしてしまうと C:\foobar には要するに誰でもが書き込めるようになってしまう。C:\foobar\foobar.exe の内容だって変更し放題だ!!
これはもう、トロイの木馬を仕込んでくれと言っているようなものじゃないのか!? 他にも、C:\foobar\bin を PATH に加えてたりしたら「DLL インジェクション、歓迎です」ってことじゃないのか!?
C:\Program Files 以下では、もちろんそんなことはない。管理者以外には、書き込み権なんて当然無い。これこそ、誰でも書き換えられる C:\ の直下にはアプリケーションなんてインストールしてはダメってことの現れなんじゃないだろうか。
もちろん、C:\opt を作るだけではダメで、ちゃんとしたパーミッションの設定をしないといけない。ポチポチ設定するのもいいが、お手軽なのは C:\Program Files の ACL をコピーすること。これには、いったん C:\Program Files\opt を作ってからこの opt を C:\ の直下に移動すればいい。ただし、C:\Program Files の下にあるときに opt の ACL 設定で「このオブジェクトの親からの継承可能なアクセス許可を含める」を外して ACL をコピーしておくのを忘れてはならない。
「そこまでするんなら C:\Program Files にインストールすればいいのに」と思われるかもしれないが、C:\ の直下へのインストールを案内しているソフトウェアには「インストール先のパス中に空白があるとダメ」というのがあったりするのである*2

*1:Windows の場合「書き込み権」にも色々あって複雑なんだけど、まあいい。

*2:XP までの「Documents and Settings」とかいう狂気の沙汰としか思えないディレクトリ名が Vista で「Users」になったのは大変な改善だ。

Consolas で M+2VM+IPAG circle で 9pt です

コーディングするときのフォントは重要だね。
結局は好みの問題なのでどれが一番なんてのはないと思うが、「すごくいいな、これ」と思うのがあったのでメモ。
http://cl.pocari.org/2007-04-06-1.html
Consolas はものすごくいいんだけど日本語がアレだな、と思っていたところに最高の組み合わせ。M+2VM+IPAG circle は濁点と半濁点の区別がつきやすいのが非常によい。
8pt から 9pt ぐらいがいい感じ。10pt だとやや大きいように感じるのと、Eclipse で 10pt にすると日本語部分だけ異様に小さくなってしまう。なんでかは分からない。
でも、Quest PuTTY*1 で 9pt にするとなぜか縦に潰れたようなメトリックスになるので、これだけは 10pt で。

*1:端末エミュレータ/SSH クライアントとしてはイマイチなんだけど、GSSAPI 認証をサポートしているのが他に見あたらない。

環境変数 DYLD_LIBRARY_PATH で Xcode の Subversion を差し替える

いろいろと訳((HTTP (HTTPS でなく) 上で SPNEGO 認証がしたい、svn help で日本語が出て欲しい、とか。))有って、ちょっとカスタマイズした Subversion を使っている。なかなかいい感じに使えていて自画自賛の心境だったが、svn コマンドだけ*1じゃダメだ。やはり、Xcode から使えないといけない。
「たしか Xcode って svn コマンド蹴ってるだけだったからチョロいよなー。」と甘く見ていたら……、違う!! ちゃんと libsvn_* の API を使っていらっしゃる((「otool -L /Developer/Library/Xcode/Plug-ins/XcodeSubversionPlugin.xcplugin/Contents/MacOS/XcodeSubversionPlugin」とかすると一目瞭然。))。
だが、その程度で慌ててはいけない。多少カスタマイズしただけだし、バイナリ互換なはずだ……、多分。「なら、LD_LIBRARY_PATH でいけるだろう」と思ったが、よく考えてみたら OS Xldd じゃなくて dyld だった。でも、世の中はよくできている。DYLD_LIBRARY_PATH とかいう同じような環境変数がちゃんとあった((詳細は dyld(1) にて。))。
最後の問題は、この環境変数をどうやって Xcode のプロセスに設定するか。WindowsLinux*2 みたいな設定箇所を探して「システム環境設定」をウロウロしたがどうにも見つからない。最悪、ターミナルから

$ DYLD_LIBRARY_PATH=/opt/subversion/lib open /Developer/Applications/Xcode.app

でもいいが……、本当に最悪だ
紆余曲折の末、http://developer.apple.com/qa/qa2001/qa1067.html に到達。OS X のことが少し嫌いになる。「環境変数なんていう泥臭いものをエンドユーザに見せないのがスマート、ってのも分からなくはないが……」と内心ボヤきつつ仕方なしにコピペしようとするに……、これ画像かよっ!! Apple のことも少し嫌いになる。写経はゴメンなので、適当に find してきた *.plist をテンプレとしてコピー。plist 要素の version 属性が 1.0 になっていて、意味もなく得した気分に浸る。
勝利を確信して、ターミナルから env を実行……したが、設定したはずの DYLD_LIBRARY_PATH が見あたらない。一緒に設定した動作確認用の環境変数はちゃんと出てきていたので、「bash がクリアしてるのか?」と ps -xE してみる。案の定、launchd*3 とその子プロセス達には DYLD_LIBRARY_PATH がちゃんと設定されていて一安心。
満を持して Xcode から Subversion リポジトリにアクセスすれば、全てが思い通りに動いて非常に満ち足りた気持ちになる。
OS XUNIX で本当に良かったよ*4

*1:GUI だけ、ってのもダメだ。

*2:というか、Gnome というべき?

*3:PID 1 のやつではなく、ログイン時に起動される方。

*4:WindowsUNIX にならないかなー。