« ^ »

Schemeを組み込む

所要時間: 約 2分

今回はSchemeを自分自身のアプリケーションに組み込んでみる。ビルドする環境はmacOSを使う。SchemeにはGNU Guileを使用し、GNU Guileは3.0をHomeBrewを使ってインストールしたものを使う。

Cのプログラムからリンクする

GNU Guileの公式ドキュメントの「5.2.2 A Sample Guile Main Program」に書いてある事を試す1

#include <stdio.h>
#include <libguile.h>

void inner_main (void *closure, int argc, char **argv) {
  scm_shell(argc, argv);
} 

int main (int argc, char **argv) {
  scm_boot_guile (argc, argv, inner_main, 0);
  printf("Ok\n");
  return 0;
}

#caption: libguileをリンクしてビルドする

gcc main.c -I/usr/local/include/guile/3.0 $(PKG_CONFIG_PATH=/usr/local/opt/guile@3/lib/pkgconfig pkg-config --libs guile-3.0)

ビルドされたファイルを実行するとGNU Guileのプロンプトが起動する。

./a.out
GNU Guile 3.0.9
Copyright (C) 1995-2023 Free Software Foundation, Inc.

Guile comes with ABSOLUTELY NO WARRANTY; for details type `,show w'.
This program is free software, and you are welcome to redistribute it
under certain conditions; type `,show c' for details.

Enter `,help' for help.
scheme@(guile-user)>
実行する

これは処理を停止するため、同時に実行させるのであれば当然他の仕組みと組合せて実行する必要がある。

pthreadでGNU Guileの処理を実行する

素直に scm_shell() を呼び出してしまうと処理がブロックされるため、pthread内で呼び出してみる。まず、簡単なpthreadの使い方をおさらいする。

#include <stdio.h>
#include <pthread.h>

void th_fn(void);

long long int grg;

int main(void) {
  pthread_t th;
  pthread_create(&th, NULL, (void *)th_fn, (void *)NULL);
  pthread_join(th, NULL);
  return 0;
}

void th_fn(void) {
  for(grg = 0; grg < 10; grg++) {
    printf("%lld\n", grg);
  }
}

コンパイラはclangを使った。このあたりのオプションなどはclangとgccでは異なる場合があるため注意が必要になる。

gcc -pthreads example_pthread.c

生成されたファイルを実行する。

./a.out
0
1
2
3
4
5
6
7
8
9

この処理の中のスレッド内部で scm_shell() を呼び出す。

#include <stdio.h>
#include <libguile.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void th_fn(void);

long long int grg;

void inner_main (void *closure, int argc, char **argv) {
  printf("FFF\n");      
  scm_shell(argc, argv);
  printf("GGG\n");        
}

int main(void) {
  pthread_t th;
  printf("AAA\n");  
  pthread_create(&th, NULL, (void *)th_fn, (void *)NULL);
  printf("BBB\n");
  for (int ii = 0; ii < 10; ii++) {
    printf("KKK\n");
    sleep(1);
  }
  pthread_join(th, NULL);
  printf("CCC\n");
  return 0;
}

void th_fn(void) {
  printf("DDD\n");  
  scm_boot_guile (NULL, NULL, inner_main, 0);
  printf("EEE\n");    
}
gcc -pthreads -I/usr/local/include/guile/3.0 $(PKG_CONFIG_PATH=/usr/local/opt/guile@3/lib/pkgconfig pkg-config --libs guile-3.0) example_pthread_guile.c

これでメインループ側を実行しながら、GNU Guileを起動できる。GNU Guileはスレッド内で起動しているため、スレッド自体はGNU Guileが起動したタイミングで入力待ちによって停止してしまうが、メインループは動いたまま pthread_join() に到達し、スレッドの終了を待つようになる。