ffcallというライブラリを使えばC言語でクロージャできるというのを一昨日知ったので試してみました。
日本語の情報は全く無いし英語の情報もちょっとしかないので多分需要が無いんだと思いますがまとめておきます。
利点
ソースへの前処理など不要で、ライブラリをリンクするだけでクロージャを実現できます。
欠点
スクリプト言語でのクロージャの使用のようなお手軽さは全く無いです。
MSVCで試したが動かず。gcc専用かもしれません。
使い方
ffcallは様々なパッケージから利用されているらしく、msys2でもパッケージが用意されていましたので、ほとんどの環境でパッケージマネージャからインストールできるかと思います。
以下の用例はSICPに倣って銀行口座を例としています。
#include <callback.h>
#include <stdio.h>
int deposit(int *account, va_alist alist) {
va_start_int(alist);
int amount = va_arg_int(alist);
*account += amount;
va_return_int(alist, *account);
}
int withdraw(int *account, va_alist alist) {
va_start_int(alist);
int amount = va_arg_int(alist);
*account -= amount;
va_return_int(alist, *account);
}
int main() {
int account1=0, account2 = 0;
int (*depo1)(int) = alloc_callback((callback_function_t)(&deposit), &account1);
int (*withdraw1)(int) = alloc_callback((callback_function_t)(&withdraw), &account1);
int (*depo2)(int) = alloc_callback((callback_function_t)(&deposit), &account2);
int (*withdraw2)(int) = alloc_callback((callback_function_t)(&withdraw), &account2);
printf("account1: %d\n", depo1(30)); // account1: 30
printf("account2: %d\n", depo2(1000)); // account2: 1000
printf("account1: %d\n", depo1(10)); // account1: 40
printf("account2: %d\n", depo2(2000)); // account2: 3000
printf("account1: %d\n", withdraw1(5)); // account1: 35
printf("account2: %d\n", withdraw2(500)); // account2: 2500
free_callback(depo1);
free_callback(depo2);
free_callback(withdraw1);
free_callback(withdraw2);
}
ffcallではクロージャのスコープに入れる変数を明示的に指定する必要がありますが、指定できるのはひとつだけですので、複数の変数をスコープに入れたい場合は構造体にまとめる必要があります。
今回の例では構造体は使わず、intの預金残高 account 変数をひとつ使います。
クロージャにする関数としてdepositとwithdrawを定義しています。
この2つの関数に対してalloc_callback()を適用してクロージャを作ります。
この例ではそれぞれaccount1、account2を指定して作成しています 。
alloc_callback()で作成したクロージャは使用しなくなったらfree_callback()を呼び出して削除します。このように C 言語らしくリソース管理は手動です。
クロージャにする関数の定義ですが、第一引数をクロージャのスコープに入れたい変数とします。 クロージャの呼び出し時の引数は可変長引数として処理するため、第2引数はva_alist型の変数とします 。
引数の処理ですが、関数の先頭でva_startで始まるマクロを呼び出します。マクロ名の末尾はクロージャーの戻り値の型を指定します。int型で残高を返したいのでva_start_intとしています。
va_argで始まるマクロで引数を取り出します。マクロ名の末尾は引数の型とします。預り金または引出金としてintを指定します。
最後にva_returnでは戻り値の型と値を指定します。
所感
コールバックを多用するGUIアプリケーションなら有用か。
(C++でいい)
リンク