3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ffcallを使ってC言語で簡単にクロージャ (C++じゃないよ!)

Last updated at Posted at 2019-04-15

ffcallというライブラリを使えばC言語でクロージャできるというのを一昨日知ったので試してみました。

日本語の情報は全く無いし英語の情報もちょっとしかないので多分需要が無いんだと思いますがまとめておきます。

利点
ソースへの前処理など不要で、ライブラリをリンクするだけでクロージャを実現できます。

欠点
スクリプト言語でのクロージャの使用のようなお手軽さは全く無いです。
MSVCで試したが動かず。gcc専用かもしれません。

使い方

ffcallは様々なパッケージから利用されているらしく、msys2でもパッケージが用意されていましたので、ほとんどの環境でパッケージマネージャからインストールできるかと思います。

以下の用例はSICPに倣って銀行口座を例としています。

main.c
#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++でいい)

リンク

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?