Help us understand the problem. What is going on with this article?

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

【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,記事に関する補足,他)
ytaki0801
``Don't feel as if the key to successful computing is only in your hands.'' -- Alan J. Perlis
http://nbk.bz/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした