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

【2020-08-19追記】[Python3版の記事](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)を作成しました．

[こちらの記事](https://qiita.com/ytaki0801/items/6a7ba91761fe6f4a49fb)の[GNU Guile](https://www.gnu.org/software/guile/)版．公式サイトのサンプルが[短すぎたり](https://www.gnu.org/software/guile/manual/html_node/A-Sample-Guile-Main-Program.html#A-Sample-Guile-Main-Program)[具体的すぎたり](https://www.gnu.org/software/guile/docs/guile-tut/tutorial.html)してとっつきにくかったので作成．記事作成の経緯その他は[元記事](https://qiita.com/ytaki0801/items/6a7ba91761fe6f4a49fb)を参照．動作確認環境は次の通り．

* 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の関数を整数の引数で実行し，実行結果をリスト構造で受け取る．

```C: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`を定義している．

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

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

```bash
$ 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インタプリタへの追加モジュールとすることができる．基本的には[公式マニュアル](https://www.gnu.org/software/guile/manual/guile.html#A-Sample-Guile-Extension)にある通りに行えば良い．たとえば，次のように抜粋・修正して`cfib.c`とする．

```Scheme: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`を生成する．

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

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

```Scheme
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記述を文字列として処理系に渡して実行する例を示す．

```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);
}
```
```shell
$ 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](https://www.gnu.org/software/guile/manual/html_node/API-Reference.html)）をベースにすれば，いろんなC言語プログラムにGuile処理系組み込めそうだなあ．とりあえず，CPythonに組み込んでみる？（まぜるな危険）．
* [Python3版](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)の記述例に合わせて全面書き直ししたら，めちゃくちゃシンプル…．Guileすげー．

##参考文献
* [GNU Guile](https://www.gnu.org/software/guile/)
* [Scripting with Guile](https://www.ibm.com/developerworks/library/l-guile/index.html)

##更新履歴
* 2020-08-20：[Python3版](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)の記述例に合わせて全面書き直し
* 2020-08-19：[Python3版の記事](https://qiita.com/ytaki0801/items/9d30c65e5edba7dcb0fe)リンク追記
* 2020-08-18：初版公開（例その１，例その２，記事に関する補足，他）