Skip to content

Document

ytomino edited this page Mar 17, 2019 · 15 revisions

Abstract

Good-bye C language, Reuse C Libraries.

残念ながら、現在のほぼすべてのOSは、APIをC言語のヘッダーという形でしか公開していません。 また、iconvやzlibなどの必須ライブラリもほぼ全てC言語で書かれています。 規格はプリプロセッサを使わなくてもよい方向へ進んできているにも関わらず、使用実態としては一向に減らないプリプロセッサの乱用も相まって、ヘッダーファイルを読み解きポーティングを行いバージョンアップにも追従するのは苦難の道であり、それ故にC言語以外の使用をあきらめないといけないのが実情です。不幸な拡大再生産はいつまで経っても終わりません。 これは、C言語以外の言語を使うときに必要な、面倒くさいヘッダーファイルのポーティング作業を行うためのツールです。

Download

Please clone from this repository with --recursive option.

git clone --recursive git://github.com/ytomino/headmaster.git

Features

Target Languages

"headmaster"は、外部呼び出しのために専用のスタブを必要としない、所謂ネイティブランゲージを対象とします。 スタティックリンクができて、外部呼び出しの構文が用意されていて、Cの全ての型を扱える言語であれば、ターゲット言語としては申し分ありません。(理論上は)

予定というか妄想としてはDとかFortranとか対応したいです。

実際に対応している処理系

gcc Ada

gcc に含まれるAdaコンパイラです。

変換後のヘッダーは全てpackage Cの下に配置します。package C自体には、既定義の型(int等)やマクロ(cpp -dMで表示されるもの)が入ります。 ディレクトリはパッケージの入れ子で表現します。

ファイル名の変換例

  • stdio.h -> C.stdio
  • sys/stat.h -> C.sys.unistd

Adaの識別子にはアンダースコアをふたつ続けられないルールがあります。 このため、アンダースコアがふたつ続いていた場合は、ひとつにしてしまいますが、もしそれで名前の衝突が起きる場合は、"q"に置き換えます。("q"はアルファベットの出現頻度で下から2番目。最下位は"z"ですが、zlibと混同しそうなため回避してます)

識別子の変換例

  • the__symbol -> the_symbol (the_symbolがない場合)
  • the__symbol -> theqqsymbol (the_symbolが既にある場合)

Adaはstrong-typingのため、名前を付けずに使った型はそれぞれ別の型とされてしまいます。そのため、匿名型には変換に際して適当な名前を付けます。

配列は、要素の型に_arrayを付けた名前になります。インデックスはsize_tです。

ポインタは、指し示す型に_ptr、_const_ptrのpostfixを付けた名前のaccess型になります。 関数ポインタは、access_XXXXXXXXといった形で名前が付けられます。 ただし、関数の引数のポインタは、匿名のaccess型を使用します。(関数ポインタを除く) タグの付いていないenum/struct/unionには、enum_XXXXXXXX/struct_XXXXXXXX/union_XXXXXXXXといった形で名前が付けられます。

opaque type (struct TAG; の形でのみ宣言されて実体が無い型)は、unconstrainedかつ代入不可能な抽象型(type TAG (<>) is limited private;)になります。

Command line options

manpage形式で書いてます。

#pragma

"headmaster"専用の#pragma群。 ソースツリーにもドキュメントを含めてます。

type

変換先の言語での対応する型を指定します。 既存のインターフェースライブラリと互換性を持たせるための#pragmaです。

Syntax

#pragma for TARGET-LANG type TYPE-NAME = "TARGET-LANG-TYPE"

Example

#pragma for Ada type int = "Interfaces.C.int"
#pragma for Ada type char * = "Interfaces.C.Strings.chars_ptr"

この例では、 int に対応する型として Interfaces.C.intchar * に対応する型として``Interfaces.C.Strings.chars_ptr`` を使用します。

(そのままですと intInteger を継承し、 char *access char として新しく宣言されます)

置き換えた型が元の型と同じように使えないと、コンパイルが通らなくなるかもです。

overload

関数に(ABIに互換性がある範囲で)オーバーロードを追加します。

Syntax

#pragma for TARGET-LANG overload SPECIFIER-QUALIFIER-LIST DECLARATOR

Example

#pragma for Ada overload void printf(char const *, char const *)
#pragma for Ada overload void printf(char const *, int, int)

通常printfの返値はintですが、それをそのまま変換してしまうと、返値を無視できない言語では使いにく、(ターゲット言語に関数をオーバーロードする機能がある場合)返値をvoidとしたオーバーロードを追加できると便利です。 また、可変長引数が使えない言語に向けて、明示的に取りうる引数のパターンを指定することもできます。 また同じ関数でも、環境によってポインタの指す先にconstが付いていたりいなかったりすると、両方の環境に向けたコードを書くのが困難な場合があります。(C言語から使う分には警告だけで通すコンパイラがほとんどでしょうが……) そんな場合にシグネチャを揃えるためにも活用できます。

あ、32ビットですと問題ないのですが、x86_64の呼び出し規約ですと可変長引数の関数と通常の関数は違いますので(raxに浮動小数点数の数が入る)、printfは呼べないです。

include

変換後のファイルに、他のファイルの定義を含めます。 「このシンボルを使うときはこのファイルを#includeするべき」とされているファイルと実際にシンボルが宣言されているファイルが異なっている場合、ターゲット言語のモジュールシステムによっては#include(やD言語のpublic import)のような連鎖が行えず、シンボルが意図したモジュール経由で使用できない場合があります。 この#pragmaは、あるヘッダーファイルの変換先モジュールに、他のヘッダーファイルで宣言されたシンボル(のエイリアス)を含めることができます。

Syntax

#pragma for TARGET "FILE1" include "FILE2"

Example

#pragma for Ada "errno.h" include "sys/errno.h"

Darwin(Mac OSX)では、"errno.h"は #include <sys/errno.h> しているだけで、実際のエラーコードは"sys/errno.h"にあります。 勿論"errno.h"が直接エラーコードを宣言している環境も存在するため、このままではエラーコードを使うために参照しなければならないモジュールが環境ごとに変わってしまいます。 この例では、"sys/errno.h"の内容を"errno.h"の変換先モジュールにも含めることで、どの環境でも"errno.h"の変換先モジュールにエラーコードが含まれるようにします。

#includeを見て自動でやれよ同じこと2回書かせるんじゃねえ……というのは私も正論と思います……。 自動でやるとファイルサイズが膨れるのが嫌とか、公開の意図なく単に#includeしているだけの関係を知らないうちに利用してしまい逆に環境依存を作ってしまいそうとか、なんか問題ありそうじゃないですか、気分だけですけど。

monolithic_include

#pragma includeと同じですが、対象のファイルを生成せずに完全に埋め込んでしまいます。

循環参照を解決するための最終兵器。

instance

マクロ定義に型を与えます。 関数マクロではオーバーロードもできます。

Syntax

#pragma instance SPECIFIER-QUALIFIER-LIST DECLARATOR'

マクロ展開を防ぐため、特別に文字列を用いて宣言してください。

Example

#define max(a, b) (((a) > (b)) ? (a) : (b))
#pragma instance int "max"(int, int)
#pragma instance float "max"(float, float)

Tutorial

...under construction...

iconv.hの変換例をアップロードしてます。

windows.hの変換例。

Applications

https://github.com/ytomino によく使うライブラリのAda bindingをいくつか置いてます。

各OSの主要ヘッダーファイルを丸ごと変換することによって、100% Adaを実現した drake が今のところ"headmaster"最大の成果。

上手く変換できない例

循環参照

a.h

typedef b_t a_t;

b.h

typedef int b_t;
#include "a.h"
typedef a_t c_t;

#pragma monolithic_includeを使ってa.hをb.hの一部として取り込んでしまえば解決出来ます。

不完全型の宣言と本体が別ファイル

a.h

struct x;

b.h

#include "a.h"
struct x { int m; };

0.13〜以降、ある程度は対応してます。

License

"headmaster"自体のライセンスはBSDLで公開します。

変換後のファイルのライセンスは、変換元のファイルのライセンスに準じます。("headmaster"自体のライセンスは関係しません!) 扱いとしては、あなたがテキストエディタで編集して地道にポーティングを行った場合と完全に同じとしてください。元ファイルのライセンスに反しない範囲で、ポーティング作業に対する著作権を主張することも自由です。(例えば元ファイルがGPLの場合は配布条件を変更できませんが、元ファイルがBSDLの場合は元々の著作権表示とライセンス条文を付けることで自由な条件を加えて再配布できます。元ファイルがPDSやNYSLであれば好き勝手できます)

ただし……変換後のファイルを配布することは推奨しません。変換後のファイルは、プラットフォームごとの条件(ポインタのサイズ等)も条件分岐(#ifdef)もすべて展開されてしまっていて、他の環境に持って行っても正常動作が期待できません。変換後のファイルを配布するよりも、ビルドプロセスの一部として、ターゲット環境ごとに変換作業をやり直したほうが良いでしょう。

Links

先達プロジェクト

SWIG
C/C++のソースファイルと専用のインターフェース定義ファイルから、各種スクリプト言語に向けたスタブを生成してくれるツールです。 対応言語に Modula-3がある のが謎。 これのみCライブラリを直接呼び出せるネイティブランゲージ。他はスクリプト言語だったりVM向けだったり ……と思ってたらDとか追加されてるし。
C2Ada
headmasterに一番近い先達。 gccに-fdump-ada-specオプション(後述)が実装されたことで、開発が放棄された模様。
GNAT GPL Edition
これに含まれるgccは、 -fdump-ada-specオプション を指定することで、C/C++のヘッダーファイルからAdaのspecを出力することができます。 環境差を吸収する手段等に欠けており、現時点では実用には耐えないと判断しました。将来的には私が"headmaster"に費やした労力は全て無駄だった……ということになるかもしれません……。
CIL
C言語を解析するライブラリ。 "headmaster"ではトランスレータの都合上CILを使うことはしませんでしたが、優れたツールです。 Who Says C is Simple? は必読。

Cプリプロセッサ

MCPP
精度の高いCプリプロセッサ。 ドキュメントに書かれている愚痴と提案は必読。
You can’t perform that action at this time.