2011-11-28

LevelDBの共有ライブラリを作る

2011年11月28日現在、LevelDBのMakefileはまだ共有ライブラリの生成に対応してないので、それについてのworkaround。何度も同じことを考えるのを防ぐための備忘用。

まず、元になるオブジェクトコードを作る。

CFLAGS="-I$HOME/opt/snappy-1.0.4/include" \
gmake CC="g++ -m64" OPT="-O2 -DNDEBUG -fPIC" \
SNAPPY_CFLAGS="-I$HOME/opt/snappy-1.0.4/include -DSNAPPY"          

各指定の簡単な解説。

最初のCFLAGSはSnappyのヘッダの位置をプリプロセッサに伝える。標準の位置にあるならこの指定は不要。build_detect_platformでのみ有効で、MakefileでCFLAGSは上書きされる。SNAPPY_CFLAGSでも同じ指定をしているのはそのため。

今回は64ビットのライブラリを作りたい(Clozure CLの64ビットバイナリからFFI経由で使いたくなった)ので、CCはg++ -m64で。GCC 4.7より前のバージョンでは、GCCをビルドするときに設定しない限り、Solaris/x86-64でもデフォルトで64ビットコードを作らない。Solaris 11の標準のGCC 4は4.5.2。

効率的に動作して欲しいので、-fPICを付ける。適当な場所が見当たらなかったので、OPTに追加。

SNAPPY_CFLAGSは上に書いているように、ヘッダの位置の指定を追加。

次に、共有ライブラリを作る。

g++ -m64 -shared -Wl,-h,libleveldb.so.0 \
db/*.o port/*.o table/*.o util/*.o \
-Wl,-R,$HOME/opt/lib -L$HOME/opt/snappy-1.0.4/lib \
-lsnappy -lpthread -lrt -o libleveldb.so.0.0

64ビットのライブラリを作るため、-m64を指定するのは同じ。

-sharedで共有ライブラリを作ると指定し、-Wl,-hでsonameを指定するためのオプションをリンカに渡す。Solarisのldでは-sonameではなく-h。あるいは--soname。

材料のオブジェクトファイルは上の例のように指定すればすべて揃う。不安ならmakeしたときにarに渡されているファイルと照らし合わせれば確認できる。

リンクするライブラリは、Snappyと、環境に依存するライブラリ。これは、makeするときにできるbuild_config.mkを見れば分かる。PLATFORM_LDFLAGSに指定されているもの。Snappyも64ビットとしてビルドされていないと、当然リンクできないので注意。Snappyが標準の場所にないので、-Wl,-RでRUNPATHも指定する。

以上。あとは適当にサーチパスに放り込んで、libleveldb.so.0とlibleveldb.soという名前のシンボリックリンクをlibleveldb.so.0.0に張れば終わり。

2011-08-31

16進ダンプ

REPLから使いたくなって書いた。

使い方は以下の通り。(unsigned-byte 8)を要素に持つシーケンスを扱える。十行ごとにヘッダが入り、アドレス表示部分は長さ(標準で8桁)とオフセットを指定できる。テキスト表示部分はstandard characterかつgraphicな文字だけ表示できる。

> (hex-dump (sb-ext:string-to-octets "string"))
========== +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F =================
00000000 : 73 74 72 69 6E 67                               | string
NIL
>

2011-08-05

cl-fn

昨日のdefaliasだけど、似たようなコードを発見した。

Ron Garretユーティリティライブラリで、

(defmacro define-synonym (s1 s2)
  `(progn
     (defun ,s1 (&rest args) (declare (ignore args)))
     (setf (symbol-function ',s1) (function ,s2))))

という関数を定義してた。

何度か目を通してるはずなのに何故気付かなかったんだ、と一瞬後悔したけど、良く見たらs2を直接function特殊形式に渡してる。これだと、関数名lambda式以外を渡すことが許されないので、純粋に別名を付けるためだけを目的としたもののようだ。セーフ。こちらの主な目的は関数合成とかの結果に名前を付けることだから、上のコードは使えない。

安心した所で、無駄にならなかったdefaliasを、関数を扱うときに良く使われるユーティリティ関数とまとめてcl-fnというライブラリにした。ついでに、lambdaを書くのに疲れてきたので、前述のRon Garretのユーティリティからfnというマクロも貰ってきた。

;; &restを使わずに引数全体を束縛できる
(funcall (fn args args) 0 1 2)
;=> (0 1 2)

;; _で引数を無視できる(警告が出ないように宣言も付く)
(funcall (fn (x _) x) 0 1)
;=> 0

なんてことができるちょっと賢いやつ。ただし、リーダには手を入れないので、

((fn (x) x) 0)

みたいなことはできない。直接lambda式を呼びたいことなんてあんまりないから問題ないとは思うけど。

ちなみに、使いたい機能だけ楽にインポートできるように、パッケージを細かく分けておいた。例えば、curryとrcurryだけ使いたい場合は、cl-fn.paパッケージだけ使えば大丈夫。全部入りが欲しいならcl-fnを使う。余分なものまでインポートしなくて良いので綺麗好きな人でも安心。

2011-08-04

defalias

Emacs Lispには、関数に別名を付けるdefaliasという関数があるんだけど、これが欲しくなったので書いてみた。

(defmacro defalias (name function-designator)
  (with-gensyms (function designator)
    `(let* ((,designator ,function-designator)
            (,function (if (functionp ,designator)
                           ,designator
                           (symbol-function ,designator))))
       (setf (symbol-function ',name) ,function)
       ',name)))

マクロにしたのはクォートを付けるのが面倒だから。第二引数はfunction designatorなので、シンボルでも関数でも大丈夫。

普通に別名を付けることもできるけど、考えている主な使い所は、合成したり部分適用した関数に名前を付けたりするとき。

;; これを
(setf (symbol-function '2*)
      (curry #'* 2))

;; こう書ける
(defalias 2* (curry #'* 2))

(2* 3)  ;=> 6

他にも誰かが似たようなものを作ってそうだったから探してみたんだけど、見付けられなかった。

2011-07-06

string=の意外な利点

今まで、string=を使わずにequalで済ませていた。性能が要求される場面ではstring=の方が良いと思うけど(equalは汎用の比較関数なので、処理の振り分けの分だけどうしても遅くなる。ただ、型宣言をすれば、処理系によってはstring=と同じ処理に最適化されるかもしれない)、そんなコードなんて全然書かない。なので、すっかり存在を忘れていた今日この頃。Ron Garretユーティリティを読んでいたら、自分にとっては意外な利点を発見した。string=は文字列とシンボルの比較に便利。

ユーティリティの中にある、

(defun ignorevar? (v)
  (and (symbolp v) (string= v "_")))

というコードを読んで気付いたんだけど、string=に直接シンボルを渡している。あれ? と思ってHyperSpecを引いたら、渡すのはstring designatorで良いらしい。string designatorっていうのは、文字とシンボルと文字列のこと。

以前書いたマクロの中で、

(defun <-p (x)
  (equal (symbol-name x) "<-"))

っていうコードがあったんだけど、string=を使えば、

(defun <-p (x)
  (string= x "<-"))

こう、ちょっとだけ短く書けて、ちょっとだけ嬉しい。大きな違いはないんだけど、ささやかな幸せ、というか。

何にせよ、勉強になった。