ポータブルなコルーチンライブラリ
が書けた!
#include <assert.h> #include <boost/preprocessor.hpp> #define GOTOC_CASE_GOTO_(Z, I, DATA) \ case BOOST_PP_ADD(I, 1): \ goto BOOST_PP_CAT(gotoc_label_, BOOST_PP_ADD(I, 1)); #define GOTOC_FUNC(NUM) \ static int gotoc_call_cnt_ = 0; \ switch (gotoc_call_cnt_) { \ case 0: \ break; \ BOOST_PP_REPEAT(NUM, GOTOC_CASE_GOTO_, dummy) \ default: \ assert(false); \ } #define GOTOC_YIELD(NUM) \ gotoc_call_cnt_ = NUM; \ return; \ BOOST_PP_CAT(gotoc_label_, NUM) :
不穏な感じ。使用例は以下のように。
#include <stdio.h> void coro1() { static int i = 0; /* cannot save local variable! */ GOTOC_FUNC(3); printf("coro1 1\n"); GOTOC_YIELD(1); printf("coro1 2\n"); for (i = 0; i < 2; i++) { GOTOC_YIELD(2); printf("coro1 3\n"); } GOTOC_YIELD(3); printf("coro1 4\n"); } void coro2() { GOTOC_FUNC(3); printf("coro2 1\n"); GOTOC_YIELD(1); printf("coro2 2\n"); GOTOC_YIELD(2); printf("coro2 3\n"); GOTOC_YIELD(3); printf("coro2 4\n"); } int main() { coro1(); coro2(); coro1(); coro2(); coro1(); coro2(); coro1(); coro2(); coro1(); return 0; }
ようするに入った回数覚えておいて進んだところまですっとばしてやろうというネタ。 GOTOC_FUNC(NUM) で NUM 個の YIELD があることを示しておいて、 GOTOC_YIELD(NUM) で NUM 番目の復帰位置であることを宣言します。もちろんスタックとか忘れてるので local 変数じゃなくて static 変数を使いましょう。
GOTOC_FUNC と GOTOC_YIELD に数字渡さにゃならんのがダメなところ。
void coro2() { #include GOTOC_INIT printf("coro2 1\n"); #include GOTOC_YIELD printf("coro2 2\n"); #include GOTOC_YIELD printf("coro2 3\n"); #include GOTOC_YIELD printf("coro2 4\n"); #include GOTOC_QUIT }
とかで良ければできると思います。
軽く追記
どうでもいいですが、久々に GCC の拡張文法
http://www-cms.phys.s.u-tokyo.ac.jp/~naoki/CIPINTRO/gccextend.html
を見ていて、なんとなく
int main() { goto f; printf("%d\n", ({ int f; f:; f = 0; })); }
とか書いてたコードを適当にふくらませて
#include <stdio.h> void f(int f) { printf("%x\n", f); } int main() { goto *&&f; f(({ int f; f?f<+f+++f++>f:f;f:f,f = ({ int f() { return f<f-f>f; } f; f(); }); for (f;f;f); })); }
とかこんな感じで処理の流れわけわからんなーとか思ってるうちに今日のことをやってみたくなったとか。
あ、あと、1.ちゃんとスタック保存する、 2.ifdef を使わないという意味でのポータブルな、ライブラリが書ける気がしてきました (本日3度目の思いつきで2度は失敗) が大変そうなので明日…いずれにせよ制限ありまくりの実用性皆無のネタですが。