インスタンシエートできないマクロをどうすんのか問題
yuniFFIはPOSIX(より正確にはいわゆるlibc)の記述を重要な目標としている。つまり、POSIXプログラムをC言語で書く状況に近いユーザビリティをScheme上に実現することが要求される。
POSIXの中には、マクロと関数のどちらで実装しても良いシンボルがいくつか存在する。yuniFFIでは、そのようなシンボルはインスタンシエートして関数化し、それに対するFFI呼び出しの形でAPIを使用することができる。
例えば、select()で使用されるFD_SET等は(名前がいかにもマクロっぽいにも関わらず)関数またはマクロであると規定されている。
File descriptor masks of type fd_set can be initialized and tested with FD_CLR(), FD_ISSET(), FD_SET(), and FD_ZERO(). It is unspecified whether each of these is a macro or a function.
このため、FFIとして公開するためには、stub内でインスタンシエートする必要がある。
void instance_FD_SET(int fd, fd_set* fdset){ // この関数をFFIで公開する FD_SET(fd, fdset) }
(実際には、yuniFFI処理系はインスタンシエートに関して特別な処理をすることは無い。単にtype0 stub http://d.hatena.ne.jp/mjt/20140720/p1 で同じ作用を持つため。)
しかし、中にはマクロを単体でインスタンシエートできないものが有る。pthread_cleanup_push / pthread_cleanup_popは、同じレキシカルスコープに書かれることを前提としたマクロとして実装しても良いことになっており、この性質によりそれぞれを単体の関数としてインスタンシエートすることはできない。
These functions may be implemented as macros. The application shall ensure that they appear as statements, and in pairs within the same lexical scope (that is, the pthread_cleanup_push() macro may be thought to expand to a token list whose first token is '{' with pthread_cleanup_pop() expanding to a token list whose last token is the corresponding '}' ).
Cygwin(newlib)やglibcではpthread_clean_pushはローカル変数としてバッファを確保してそのアドレスをpushし、cleanup_popでそのスタックをpopするという実装になっている。このため、単体でインスタンシエートしようとしてもエラーになる。
oku@spring ~ $ cat check.c #include <pthread.h> void instance_pthread_cleanup_push(void (*routine)(void*), void* arg){ pthread_cleanup_push(routine, arg); } oku@spring ~ $ cc check.c check.c: In function ‘instance_pthread_cleanup_push’: check.c:6:1: error: expected declaration or statement at end of input } ^
yuniFFIというか、常識的なFFIシステムは全てこの手のシンボルを良く処理できない。もちろん殆どのケースで処理できる必要は無いが、実際にどのくらいこのようなシンボルが有るのかは何とも言えない。