LoginSignup
0
0

More than 3 years have passed since last update.

GNU GuileのScheme処理系APIをC言語から利用する記述のサンプル

Last updated at Posted at 2020-08-18

【2020-08-19追記】Python3版の記事を作成しました.

こちらの記事GNU Guile版.公式サイトのサンプルが短すぎたり具体的すぎたりしてとっつきにくかったので作成.記事作成の経緯その他は元記事を参照.動作確認環境は次の通り.

  • Raspberry Pi 4 Model B (OSプリインストール),gcc 8.3.0,GNU Guile 3.0.4
  • Ubuntu 18.04 Desktop x86_64,gcc 7.5.0,GNU Guile 2.2.3
  • Termux 0.98 (aarch64-linux-android),clang version 10.0.1,GNU Guile 2.2.6

C言語記述例

  • Scheme用の関数を定義する.
  • 定義した関数をSchemeから利用できるようにする.
  • 定義した関数を使用するSchemeの関数定義を含むSchemeファイルを読み込む.
  • Schemeの関数を整数の引数で実行し,実行結果をリスト構造で受け取る.
guile-cfunc.c
#include <stdio.h>
#include <libguile.h>

/* Scheme用関数の定義 */
SCM cfib(SCM var)
{
  int f1 = 0, f2 = 1, t;
  for (int n = scm_to_int(var); n > 0; n--) {
    t = f1; f1 = f2; f2 = t + f2;
  }
  return (scm_from_int(f1));
}

/* ユーザ設定ファイルを模したファイル */
#define APPINITSCM ".appinit.scm"

int main(int argc, char **argv)
{
  /* Scheme処理系の初期化 */
  scm_init_guile();

  /* 定義した関数をSchemeから利用できるようにする設定 */
  scm_c_define_gsubr("cfib", 1, 0, 0, cfib);

  /* ホームディレクトリにあるAPPINITSCMの読み込み */
  char appinitfile[128];
  sprintf(appinitfile, "%s%s%s", getenv("HOME"), "/", APPINITSCM);
  scm_c_primitive_load(appinitfile);

  /* Schemeの関数を整数の引数で実行し,実行結果をSchemeオブジェクトで取得 */
  SCM proc = scm_variable_ref(scm_c_lookup("cfib-itr"));
  SCM eRet = scm_call_1(proc, scm_from_int(10));

  /* 戻り値をリスト構造とみなして参照し,各要素を表示 */
  for (int i = 0; i < scm_to_int(scm_length(eRet)); i++)
    printf("%d ", scm_to_int(scm_list_ref(eRet, scm_from_int(i))));
  printf("\n");

  return (0);
}

ユーザ設定ファイルを模したファイルの例は次の通り.C言語で定義した関数cfibを使用するSchemeの関数cfib-itrを定義している.

$ cat $HOME/.appinit.scm
(define cfib-itr (lambda (n) (map (lambda (x) (cfib x)) (iota (+ n 1)))))

コンパイルおよび実行例は次の通り.

$ cc `pkg-config guile-3.0 --cflags` -o guile-cfunc guile-cfunc.c `pkg-config guile-3.0 --libs`
$ ./guile-cfunc 
0 1 1 2 3 5 8 13 21 34 55 

補足:C言語関数定義のモジュール化

Scheme用関数の定義やモジュールの設定部分は,そのままSchemeインタプリタへの追加モジュールとすることができる.基本的には公式マニュアルにある通りに行えば良い.たとえば,次のように抜粋・修正してcfib.cとする.

cfib.c
#include <libguile.h>

SCM cfib(SCM var)
{
  int f1 = 0, f2 = 1, t;
  for (int n = scm_to_int(var); n > 0; n--) {
    t = f1; f1 = f2; f2 = t + f2;
  }
  return (scm_from_int(f1));
}

void init_cfib() {
  scm_c_define_gsubr("cfib", 1, 0, 0, cfib);
}

このファイルから次のようにコンパイルして,動的ライブラリclib.soを生成する.

$ cc -Wall -fPIC `pkg-config --cflags guile-3.0` -shared -o cfib.so cfib.c

このcfib.soがあるディレクトリでGuileを起動すると定義関数が利用できる.

guile> (load-extension "./cfib" "init_cfib")
guile> (cfib 10)
$1 = 55
guile> (map cfib (iota 10))
$2 = (0 1 1 2 3 5 8 13 21 34)

補足:引数および戻り値がリスト構造のC言語関数定義

併せて,Scheme記述を文字列として処理系に渡して実行する例を示す.

guile-cfunc-listargs.c
#include <stdio.h>
#include <libguile.h>

SCM cfib(SCM args)
{
  int n  = scm_to_int(scm_list_ref(args, scm_from_int(0)));
  int f1 = scm_to_int(scm_list_ref(args, scm_from_int(1)));
  int f2 = scm_to_int(scm_list_ref(args, scm_from_int(2)));

  int t; for (; n > 0; n--) { t = f1; f1 = f2; f2 = t + f2; }

  SCM ret = scm_list_3(scm_from_int(f1),
                       scm_from_latin1_string("clib"),
                       scm_from_latin1_string("result"));
  return (ret);
}

int main(int argc, char **argv)
{
  scm_init_guile();
  scm_c_define_gsubr("cfib", 1, 0, 0, cfib);
  scm_c_eval_string("(display (cfib '(10 0 1))) (newline)");
  return (0);
}
$ cc `pkg-config guile-3.0 --cflags` -o guile-cfunc-listargs guile-cfunc-listargs.c `pkg-config guile-3.0 --libs`
$ ./guile-cfunc-listargs
(55 clib result)

備考

記事に関する補足

  • このサンプル(+API Reference)をベースにすれば,いろんなC言語プログラムにGuile処理系組み込めそうだなあ.とりあえず,CPythonに組み込んでみる?(まぜるな危険).
  • Python3版の記述例に合わせて全面書き直ししたら,めちゃくちゃシンプル….Guileすげー.

参考文献

更新履歴

  • 2020-08-20:Python3版の記述例に合わせて全面書き直し
  • 2020-08-19:Python3版の記事リンク追記
  • 2020-08-18:初版公開(例その1,例その2,記事に関する補足,他)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0