こんにちは、鈴木です。
「C言語からGaucheを使おう! (1)」の続編です。
前回は何もしないプログラムをコンパイル&実行するところまで行いました。
今回は、数値や文字列などの基本的な値の生成と、それを表示する方法を調べます。
数値型
gauche.h の中に、数値型の値を生成する SCM_MAKE_INT というマクロを見つけました。
これが足がかりになりそうです。
1 |
#define SCM_MAKE_INT(obj) SCM_OBJ(((intptr_t)(obj) << 2) + 1) |
SCM_OBJ の定義は以下のようになっており、ScmObj にキャストされていました。
1 |
#define SCM_OBJ(obj) ((ScmObj)(obj)) |
ScmObj が Gauche が扱う値を表す、ということでしょうか。
SCM_MAKE_INT の周辺を見ると、オブジェクトが数値型であるか判定する SCM_INTP や、数値オブジェクトから値を取り出す SCM_INT_VALUE も定義されていました。
1 2 3 4 5 6 7 |
/* * FIXNUM */ #define SCM_INTP(obj) (SCM_TAG2(obj) == 1) #define SCM_INT_VALUE(obj) (((signed long int)SCM_WORD(obj)) >> 2) #define SCM_MAKE_INT(obj) SCM_OBJ(((intptr_t)(obj) << 2) + 1) |
サンプルコードを書いて動作を確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> #include <gauche.h> int main() { ScmObj value; GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); value = SCM_MAKE_INT(123); printf("INTP: %s\n", SCM_INTP(value) ? "true" : "false"); printf("INT_VALUE: %ld\n", SCM_INT_VALUE(value)); return 0; } |
コンパイル&実行します(※「C言語からGaucheを使おう! (1)」で準備した Makefile を使います)。
1 2 3 |
> make run INTP: true INT_VALUE: 123 |
ここまでの内容で、
- 数値オブジェクトを生成する方法(SCM_MAKE_INT)
- 数値オブジェクトであるか判定する方法(SCM_INTP)
- 数値オブジェクトから値を取り出す方法(SCM_INT_VALUE)
が分かりました。
その他のデータ型
gauche.h には数値以外のためのマクロも定義されていました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
> grep -rE '#define\s+SCM_MAKE_' -B2 gauche.h #define SCM_BOOLP(obj) ((obj) == SCM_TRUE || (obj) == SCM_FALSE) #define SCM_BOOL_VALUE(obj) (!SCM_FALSEP(obj)) #define SCM_MAKE_BOOL(obj) ((obj)? SCM_TRUE:SCM_FALSE) -- #define SCM_INTP(obj) (SCM_TAG2(obj) == 1) #define SCM_INT_VALUE(obj) (((signed long int)SCM_WORD(obj)) >> 2) #define SCM_MAKE_INT(obj) SCM_OBJ(((intptr_t)(obj) << 2) + 1) -- #define SCM_CHARP(obj) ((SCM_WORD(obj)&0xff) == 3) #define SCM_CHAR_VALUE(obj) SCM_CHAR(((unsigned long)SCM_WORD(obj)) >> 8) #define SCM_MAKE_CHAR(ch) SCM_OBJ((long)((ch) << 8) + 3) -- #define SCM_MAYBE_P(pred, obj) (SCM_FALSEP(obj)||(pred(obj))) #define SCM_MAYBE(unboxer, obj) (SCM_FALSEP(obj)?NULL:(unboxer(obj))) #define SCM_MAKE_MAYBE(boxer, obj) ((obj)?(boxer(obj)):SCM_FALSE) |
BOOL、INT、CHAR、MAYBE がありました。MAYBE は基本的な値ではなさそうなので、今回は深入りしません。
BOOL型
BOOL型についても、サンプルコードで確認してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> #include <gauche.h> int main() { ScmObj value; GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); value = SCM_MAKE_BOOL(1); printf("BOOLP: %s\n", SCM_BOOL_VALUE(value) ? "true" : "false"); printf("BOOLVALUE: %d\n", SCM_BOOL_VALUE(value)); return 0; } |
実行すると、以下の出力が得られました。
1 2 3 |
> make run BOOLP: true BOOLVALUE: 1 |
BOOL型の値は定数 SCM_TRUE、SCM_FALSE も定義されていますので、次のように書くこともできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <stdio.h> #include <gauche.h> int main() { ScmObj value; GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); /* value = SCM_MAKE_BOOL(1); */ value = SCM_TRUE; printf("BOOLP: %d\n", SCM_BOOL_VALUE(value)); printf("BOOLVALUE: %d\n", SCM_BOOL_VALUE(value) ? "true" : "false"); return 0; } |
文字型
文字型は次のように扱います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> #include <gauche.h> int main() { ScmObj value; GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); value = SCM_MAKE_CHAR('A'); printf("CHARP: %s\n", SCM_CHARP(value) ? "true" : "false"); printf("CHAR_VALUE: %c\n", (int)SCM_CHAR_VALUE(value)); return 0; } |
実行すると以下の出力が得られました。
1 2 3 |
> make run CHARP: true CHAR_VALUE: A |
文字列型
文字列型については gauche.h ではなく gauche/string.h に SCM_MAKE_STR がありました。
1 2 3 4 5 6 |
#define SCM_MAKE_STR(cstr) \ Scm_MakeString(cstr, -1, -1, 0) #define SCM_MAKE_STR_COPYING(cstr) \ Scm_MakeString(cstr, -1, -1, SCM_STRING_COPYING) #define SCM_MAKE_STR_IMMUTABLE(cstr) \ Scm_MakeString(cstr, -1, -1, SCM_STRING_IMMUTABLE) |
文字列は今まで見てきたデータ型と比べて複雑な構造をしているようで、SCM_STRING_COPYING や SCM_STRING_IMMUTABLE などのフラグを持つようです。
gauche/string.h 中のコメントから、変更可能(mutable)な Scheme 文字列をマルチスレッド環境で正しく動くようにするためにひと工夫していることが分かりました。
サンプルコードを書いて動作確認します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <stdio.h> #include <gauche.h> int main() { ScmObj value; GC_INIT(); Scm_Init(GAUCHE_SIGNATURE); value = SCM_MAKE_STR_IMMUTABLE("Hello, Gauche!"); printf("STRINGP: %s\n", SCM_STRINGP(value) ? "true" : "false"); printf("%s\n", SCM_STRING_CONST_CSTRING(value)); return 0; } |
文字列の生成には、SCM_MAKE_STR_IMMUTABLE を使用しました(引数にはリテラルの "Hello, Gauche" を与えており、C言語では文字列リテラルを変更することは NG のため)。
値の取得方法を調べていくと、いくつかの関数やマクロが定義されていました。
- Scm_GetString 関数は変更可能な文字列を返す(内部的に文字列のコピーが発生する)
- Scm_GetStringConst 関数は変更不可能な文字列を返す。
- SCM_STRING_CONST_CSTRING は Scm_GetStringConst(SCM_STRING(obj)) に展開されるマクロ(ScmObj から ScmString へのキャストの手間を省略できる)。
ここでは読み取り専用で参照できれば十分のため、SCM_STRING_CONST_CSTRING で値を取得するようにしました。
実行すると、以下のようになります。
1 2 3 |
> make run STRINGP: true Hello, Gauche! |
まとめ
今回調べてみてわかったことをまとめます。
- 基本は ScmObj
- SCM_MAKE_xxx から始まるマクロで基本的な値を生成する。(例:SCM_MAKE_INT)
- SCM_xxx_VALUE で値を取得する。(例:SCM_INT_VALUE)
- SCM_xxxP で方の判定を行う。(例:SCM_INTP で数値型であることを判定する)
- まずは gauche.h を読む。
- 文字列のように別のファイル(gauche/string.h)を読む必要もある。
少しずつ前進していますが、まだまだ分からないことがたくさんあります。
今後はリストを扱う方法や Schemeコードを評価する方法、Schemeコード内の手続きを呼び出す方法、C言語コードとSchemeコード間で値をやりとりする方法あたりから調べようと思います。