API記述の要求

C言語APIを記述するフォーマット といっても、なかなか難しい。いわゆるLL言語と違って、Cには言語組み込みのモジュールシステムが存在するわけでないため、その使い方もあまりモデル化されていない。このため、既存のAPIを考察して必要な要素を調べておく必要がある。

既存のAPI記述

例えば、OpenGLは既にXMLベースのAPI記述に移行している。Khronosが発行しているOpenGLヘッダは、XML記述からPythonスクリプトを通して生成されている。

        <command>
            <proto>void <name>glMultiTexGendvEXT</name></proto>
            <param group="TextureUnit"><ptype>GLenum</ptype> <name>texunit</name></param>
            <param group="TextureCoordName"><ptype>GLenum</ptype> <name>coord</name></param>
            <param group="TextureGenParameter"><ptype>GLenum</ptype> <name>pname</name></param>
            <param len="COMPSIZE(pname)">const <ptype>GLdouble</ptype> *<name>params</name></param>
        </command>

このXML片は、関数glMutliTexGendvEXTを定義している。KhronosのAPI XMLC言語ヘッダをXMLで修飾したような形式をしている。
UCIDも、これと同等の記述性を持たせる必要がある。つまり、

  • APIグループのサブセット化、拡張

OpenGLはいくつかのAPIグループで構成されている。つまり、OpenGL ES1.xとOpenGL ES 2.xは互いに互換性が無く(1.xの方がサポートする関数の数は多いが、2.xはシェーダ言語等が追加されている)さらにそれらのスーパーセットとしてOpenGL 4.xが存在する(いわゆるDesktop OpenGL)。
また、OpenGLは拡張(extensions)と呼ばれる仕組みで、ベンダ固有のAPIを一定のフォーマットで記述できるようにする仕組みがある。
リポジトリのgl.xmlはこれらを全てまとめて一つのXMLに記述している。つまり、APIのサブセットや拡張を記述するための仕組みが予め備わっている。

  • パラメタの拘束

↑の例では引数paramsはCOMPSIZE(pname)のlenを持つことになっている。他にも定数等が記述されることもある。ただし、これらの拘束条件は現状どこにも使用されていない。

XMLAPIエイリアス名やvariantを記述することが出来る。これによって、拡張が標準に取り込まれるなどしてリネームされた場合も、両方の名前を1箇所で記述することができるようになっている。

API様式による要求

↑のOpenGL XMLOpenGL APIにしか事実上適用できない。そもそも、OpenGLは元来ネットワーク透過のIPCとみなすこともでき、単にそれに対するIDLでしかない。
世間のC言語APIに対応するためには、まだまだ機能の追加が必要になる:

  • C言語マクロによるvisibility

POSIXは、クライアントアプリケーションが_POSIX_C_SOURCEマクロを適切に設定することを期待している( http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_02 "For the C programming language, shall define _POSIX_C_SOURCE to be 200809L before any header is included")。また、sync()関数のような一部の関数はXSI拡張と位置づけ、_POSIX_C_SOURCEの代わりに_XOPEN_SOURCEが必要になる。(実際のlibc実装では、さらに上位のOS固有定義が有ることが多い。FEATURE_TEST_MACROS(7)。http://linuxjm.sourceforge.jp/html/LDP_man-pages/man7/feature_test_macros.7.html )
Win32では、アプリケーションが期待するWindowsAPIバージョンをWINVERマクロとして設定することを要求している。
POSIX、Win32の両者とも、APIのオーバーライドが存在する。glibc等の実装では、_FILE_OFFSET_BITSを値64で定義することで、32bitシステムでもoff_tのような型の64bitバージョンを使用できる。Win32では、UNICODEを定義することで、TCHAR型などがUnicode(wchar)になり、個々のAPIUnicode版にリダイレクトされる。

  • COM風(C++風)のインターフェース

同じKhronosであっても、OpenMAX/OpenSLはCOM風のインターフェースを採用している。これらは、APIがエクスポートするC関数は必要最小限に押さえられ、オブジェクトに対するQueryInterfaceによって、関数ポインタの並んだ構造体(〜Itf)を受け取り、これを呼び出すことで実際の機能を使用できる。

  • callbackとユーザポインタ

一部のC APIはcallbackを取る。この時に、callbackに対してユーザポインタを渡すことができるAPIが一般的で、これを直接的にサポートすることでトランポリンコードの生成が不要になるケースがある。
標準Cのqsortのようなユーザポインタを持たないAPIは、トランポリンコードを生成しないとcallback固有のデータを保持することが出来ない。通常のケースでは、callbackからホストSchemeを呼び出すためにはVMなり環境のポインタを持つ必要があるので、これを渡す必要からトランポリンコードを生成する必要がある。(ie. トランポリンコードは、ユニークなcallback関数アドレスを得るために使える。しかし、ユーザポインタを渡すことが出来るならば本質的にcallback関数は個々のcallback呼び出し規約について一つで十分であり、渡されたユーザポインタにもとづいて内部でディスパッチすることができる) qsortに関しては、glibc等がqsort_r関数としてユーザポインタを持つvariantを提供していて、C1Xではqsort_sが提供される見込。
トランポリンコードの生成は常に行えるわけではない。つまり、実行可能ページを割り当てる必要が有るため、一部のプラットフォームではそもそも許可されていない。

  • ABI互換の拡張

Proprietaryなライブラリの多くは、クライアントコードの再コンパイル無しでライブラリのアップデートが行えるように考察している。たとえば、WinAPIの一部の構造体はcbSizeメンバを持っていて、APIに渡す前にその値を適切なsizeofに設定することを要求している。
Undefinedな値がAPIに渡ることを防ぐために、構造体のzero fillを要求しているライブラリもある。undefinedな値が渡らないように努力することで、プログラムが"偶然"動くのを防ぐことができ、ライブラリコードのアップデートによる被害を抑える可能性がある。