Gauche:c-wrapper関連

Gauche:c-wrapper関連

c-wrapper関連

c-wrapper は、Gauche から C言語のライブラリを呼び出せるようにするモジュールです。


c-wrapper の情報

hamayama(2019/12/23 12:33:13 UTC)


c-wrapper-mg (c-wrapper を MinGW 上で動かす) (実験中)

https://github.com/Hamayama/c-wrapper-mg

hamayama(2014/10/19 14:43:51 UTC)(2014/11/01 04:43:28 UTC)
(2014/11/24 05:01:49 UTC)


その他 調査のメモ等

<c-wrapperの調査>

doc フォルダの c-wrapper-refj.html がリファレンスマニュアル。


c-load-library
  ターゲットの C のライブラリを dlopen (MinGW では LoadLibrary) で読み込む。

  ターゲットの C のライブラリは共有ライブラリである必要がある。
  このため、例えば Cランタイムの静的ライブラリに属する関数
  (例えば MinGW の math.h の tgamma 関数 (実体は libmingwex.a の中にある) 等)
  は、直接呼び出すことはできない(と思う)。

  一方、MinGW での msvcrt.dll のようにデフォルトで読み込まれる
  共有ライブラリの関数 (printf 等) は、
  c-load-library を実行しなくても、
  c-include でヘッダファイル (stdio.h 等) を読み込むだけで使用できる。


c-include
  C のヘッダファイルをパーズして、
  関数・グローバル変数・定数・型定義・マクロを、カレントモジュールに定義する。

  ただし、事前に cwcompile により生成した共有ライブラリが存在する場合は、
  上記処理は行わず、共有ライブラリを読み込む。
  (この共有ライブラリを スクリプトと同じフォルダに置いて使う場合は、
   スクリプトの先頭に (add-load-path "." :relative) が必要)

  定義された関数は、内部で libffi を使用してターゲットの C のライブラリを
  呼び出す仕組みになっている。

  ヘッダファイルのパーズについては、
  まず GCC のプリプロセッサを呼び出してプリプロセスを行い、
  その結果に対して解析を行うようになっている。


c-load
  c-load-library と c-include を一度に行う。


c-ld
  (c-load-library '() :option option-string) と同じ。


cwcompile (ツール)
  C のヘッダファイルをパーズして共有ライブラリを作成するツール。

  C のヘッダファイルを解析して stub ファイルを生成した後、
  gauche-package compile を呼び出して .stub → .c → .o → .dll と変換している。
  gauche-package compile は .stub を .c に変換した後は、GCC を呼び出している。


その他
(1) cwcompile により生成した共有ライブラリは、
    単に dynamic-load で読み込んで使おうとするとエラーになる。
    しかし、c-wrapper の内部では dynamic-load で読み込んでいるようなので、
    c-wrapper 内の情報を使って動作する通常の共有ライブラリのようである。

(2) C のヘッダファイルにある以下のようなマクロは Scheme 側で使用できる。
      #define addmacro(x, y) ((x) + (y))
    しかし、以下のようなマクロは使用できない(と思う)。
      #define swap(x, y) do { int t; t=x; x=y; y=t; } while (0)
    すなわち、ヘッダファイル中のマクロには、使用できるものと使用できないものがある(と思う)。

(3) C のヘッダファイルにある以下のような関数は Scheme 側で使用できる。
      int addfunc (int x, int y) {
        return x + y;
      }
    cwcompile 後の stub ファイルをみると、関数が define-cproc に逆コンパイルされているのが分かる。
    ただし、switch や goto は未対応らしい。
    すなわち、ヘッダファイル中の関数には、使用できるものと使用できないものがある(と思う)。

(4) C のライブラリをうまく呼び出せない場合は、
      (a) C のヘッダファイルがうまく読み込めていない
      (b) C のライブラリがうまく読み込めていない
      (c) Scheme 側の記述が誤っている
    あたりが考えられる。
    (a) だと、例えば関数名が unbound variable エラーになる。
    この場合は、内容を削った小さなヘッダファイルを用意して切り分ける等が考えられる。
    (b) だと、例えば can't load xxx エラー や function xxx is not found エラーになる。
    この場合は、ライブラリのサーチパス等をチェックする。
    (c) は、例えば C 言語のポインタがからんでくると Scheme でどう書けばよいかが難しい。
    c-wrapper の testsuite フォルダにある cwrappertest.scm の
    "test (setter ref) for a pointer" の例が参考になるかもしれない。
    ただ、一見動いてもメモリやハンドルの使用量が増えていくケースもあるので注意が必要。
    (自分もこのあたりがよく理解できていない)


(5) C のライブラリが別スレッドを生成していて、そこからコールバック関数を呼び出している場合、
    Scheme の手続きをコールバックとして登録すると、呼び出し時にエラーになる (Segmentation Fault)。
    これは、別スレッドには Gauche の VM が存在しないためである。
    この場合は、c-wrapper では対応できない(と思う)。
    拡張モジュールを作って対応する場合は、以下のページが参考になるかもしれない。
    http://comments.gmane.org/gmane.lisp.scheme.gauche/857
    (Scm_NewVM, Scm_AttachVM で Gauche の VM を用意する必要がある)
    https://github.com/aharisu/Gauche-SDL/blob/master/src/mixer/sdl-mixerlib.stub
    (mix-channel-finished という手続きでコールバックを使用している)
    http://www.moz.ac.at/sem/lehre/lib/cm/CM.app/Contents/Resources/osc/osc-recv.c
    (C のサンプル?)

(6) c-include や c-load で生成した関数等の定義を他のモジュールでも使いたい場合は、まず、
      (define-module xxxdefs (export-all))
    として定義情報用のモジュールを作成する。
    そして c-include または c-load のキーワード引数に
      :module (find-module 'xxxdefs)
    を指定して呼び出すようにする。
    すると、定義情報がモジュール xxxdefs に格納されるので、他のモジュールで
      (import xxxdefs)
    とすれば使用できる。


(7) c-load の引数を動的に生成すると、実行時や cwcompile 時にエラーになる。
    引数は固定にした方がよさそうである。
    また、cond-expand による場合分けの中で c-load すると、cwcompile が c-load を認識しない。
    これについては、cond-expand では別ファイルを load するようにして、
    別ファイルの中に c-load を書き、それを cwcompile でコンパイルすれば、
    うまくいくようである。

hamayama(2014/10/19 14:43:51 UTC)(2014/11/19 00:11:40 UTC)
(2014/11/20 04:52:41 UTC)(2015/01/09 10:46:46 UTC)
(2015/01/23 11:58:53 UTC)(2016/10/14 12:30:13 UTC)

More ...