LoginSignup
1
0

More than 1 year has passed since last update.

C言語でも call/cc したい

Last updated at Posted at 2021-05-13

はじめに

ド深夜に目が覚めて、ストゼロ飲みながら書きました。
GNU 拡張の statement expression を使っているので、gcc でコンパイルしてください。
たぶん30分くらいでバーって書いたので、バグがあったら教えてください (気が向いたら直します)

このヘッダを include すれば call/cc できます。

書きました。
(最初は __ret を volatile にするのを忘れていて、コメントでバグの指摘を頂いて修正しました)

callCC.h
#ifndef CALLCC_H
#define CALLCC_H
#include <setjmp.h>

typedef struct {
    jmp_buf *buf;
    volatile void *p_ret;
} continuation;

/* Evaluate given expression with current continuation          */
/* __cont : name of current continuation                        */
/* __typ  : type of callCC expression                           */
/* __expr : expression to evaluate with current continuation    */
#define callCC(__cont, __typ, __expr) ({    \
        volatile __typ __ret;               \
        jmp_buf __buf;                      \
                                            \
        continuation __cont;                \
        __cont.p_ret = &__ret;              \
        __cont.buf = &__buf;                \
        if(setjmp(__buf) == 0){             \
            __ret = __expr;                 \
        }                                   \
        __ret;                              \
    })

/* Ignore current continuation and use given continuation   */
/* __cont  : continuation                                   */
/* __typ   : type of associated call/cc                     */
/* __dummy : just a dummy to deceive type checker           */
/*           that never get evaluated                       */
/* __val   : value passed to given continuation             */
#define useCont(__cont, __typ, __dummy, __val) ({   \
        *((volatile __typ *) __cont.p_ret) = (__val);        \
        longjmp(*(__cont.buf), 1);                  \
        __dummy;                                    \
    })
#endif /* CALLCC_H */

テスト

main.c
#include <stdio.h>
#include "callCC.h"
#define dummy 0

int func(continuation cont, int x){
    int a;
    a = 5 + useCont(cont, int, dummy,
            x + 25
        );
    return 100000;
}

int main(int argc,char **argv){
    int a;
    /* test1 */
    a = 5 + callCC(cont, int,
                10*10
            );
    printf("%d\n", a);

    /* test2 */
    a = 5 + callCC(cont, int,
                10 * 10 * useCont(cont, int, dummy, 20)
            );
    printf("%d\n", a);

    /* test3 */
    a = 5 + callCC(cont0, int,
            3 + callCC(cont1, int,
                10 + useCont(cont0, int, dummy, 7 + 9)
            )
        );
    printf("%d\n", a);

    /* test4 */
    printf("%d\n", 7 + callCC(cont, int, 100 + func(cont, 10)));
}
実行、
$ gcc main.c
$ ./a.out
105
25
21
42

おわりに

いいんじゃないっすかね

追記

scheme とかの call/cc で取り出した継続って、大域変数にバインドして、call/ccの外側からも呼び出せるんですね。

知らんかった、そこまで出来とらんかった。
ぴえん。

1
0
5

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
1
0