週刊nmosh - UDPサポート / makeIntegerとC++オーバーロード の2

まぁ、マイペースで。。

UDPサポートと入出力の表現パターン

nmoshの非同期I/OはTCPに加えてUDPをサポートした。まだWinsockのみでBSD Socketはそのうち。pcapによるrawパケット入出力もそのうちサポートするつもり。
TCPは通常のファイルI/Oやpipeと同様の抽象化を使うことが出来る。TCPはストリーム指向の1対1の通信なので、(OOBのような滅多に使われない状況を除けば)一度確立した接続を単にScheme的なポートに見せることで十分にインターフェースできる。
UDPの場合は、リクエストごとに異なる宛先にデータを送ることができるし、逆に受信ごとにことなるアドレスからデータを受け取る可能性がある。
というわけで、今回はデータとアドレスは分け、並列に扱うことにした。例えば、受信リクエストはアドレスバッファとデータバッファの両方を渡す。毎回両方のバッファを扱わないといけないのは一見面倒だが、その辺はSchemeの記述力でどうにかして欲しいところ。
... そう考えると、Schemeの標準的なI/O手法は"ポート"と呼ぶ専用のオブジェクトに専用の手続きを作用させるようにデザインされている。
対して、今回のUDPAPIを含めnmoshのネットワークI/Oは、入力用のコールバックを渡し、出力用のクロージャを受け取るように作っている。つまり:

(define (read-callback buf) ;; データ受信用コールバック。パケットの到着毎に呼ばれる
  (display buf))
(define sender (open-connection TARGET read-callback))
;; 実際のパケット送信
(seq (=> sender DATA => result)
     ...)

そして、接続を破棄したい場合はデータの代わりに#fを渡す。なので、この方法だとデータとして#fを送りたいケースで困る。実際、message pack RPCで地味に困った。
どちらの方法が有利なのかは絶妙な問題に思える。ただ、手続きを返す方法はIDEと統合しづらいというごもっともな問題がある。型の有る言語ならこの点で困ることは無いが、少なくともSchemeではarity程度の情報しか提供できない。

C++オーバーロード の2

makeInteger関数は様々なタイプでOverloadされている割に、Win64のような絶妙な環境でambiguous overloadとなってコンパイルできないケースが多かった。
結局、まずは(u)int32と(u)int64を定義するようにしてみた。

まぁ、通常のシチュエーションなら、常識的な整数型はこれらにキャストできるので問題無かった。ただ、これだとDarwinビルドを壊していた。。というわけで:

int、long int、long long intの3種類を用意することにした。IQが下がる感じのコードだけど、これなら殆どのケースで問題無いだろう。。つまり、標準で提供されている型は全てこれらの3種類からの派生になっているだろうという仮定をする。Darwinではuint32がint、uint64がlonglongで、size_tがulongなのでint32/64だけではオーバーロードを解決できなくなっていた。