もちっとfiber
この前は id:toge:20051002#1128266791 でした。
id:shinichiro_h:20050628#1119942495 とやっていることが一緒ですね。
しかもこっちの方がswitch文も自由に使えて素敵デス。
この下、とっても長いしあんまり意味ないので注意して下さい。
switch(line_number_) { case 0: // 実コード line_number_ = 1; return; case 1: // 実コード line_number_ = 2; return; case 2: // 実コード line_number_ = 3; return; case 3: // 実コード line_number_ = 4; return; }
というのが私ゃの阿呆なコードです。
switch(line_number_) { case 0: goto fiber_label_0; return; case 1: goto fiber_label_1; return; case 2: goto fiber_label_2; return; case 3: goto fiber_label_3; return; } fiber_label_0: // 実コード line_number_ = 1; return; fiber_label_1: // 実コード line_number_ = 2; return; fiber_label_2: // 実コード line_number_ = 3; return; fiber_label_3: // 実コード line_number_ = 4; return;
というのがshinichiro_hさんのコード。うーん、ちゃんと実コードにswitch文書けますね。
コンパイラがgoto文は省略してくれるので生成コードはテーブル化されます。素敵ですね。
ということで参考にさせて貰いました。
#ifndef FIBER_H__ #define FIBER_H__ #include#include #define FIBER_CASE_GOTO_(Z, I, DATA) \ case BOOST_PP_ADD(I, 1): \ goto BOOST_PP_CAT(fiber_label_, BOOST_PP_ADD(I, 1)); \ #define COBEGIN(number) \ public: \ void operator()() \ { \ switch (line__coroutine_) \ { \ case 0: \ break; \ BOOST_PP_REPEAT(number, FIBER_CASE_GOTO_, dummy); \ default: \ BOOST_ASSERT(false); \ } \ #define COSTOP() \ { \ live__coroutine_ = false; \ return; \ } \ #define COEND() \ COSTOP(); \ } \ #define YIELD(id) \ { \ Coroutine::line__coroutine_ = id; \ return; \ } \ BOOST_PP_CAT(fiber_label_,id): \ #define RESUME(C) (C(), C.isLive()) //-------------------------------------------------------------------------------- struct Coroutine { protected: int line__coroutine_; bool live__coroutine_; public: Coroutine() : line__coroutine_(0), live__coroutine_(true) {} public: bool isLive() { live__coroutine_; } virtual void operator()() { return; } }; #endif
というヘッダファイルを書いて、以下のように使うと。
struct Nat : public Coroutine { int c; COBEGIN(5) { YIELD(1); c = 0; YIELD(2); for(;;) { YIELD(3); printf("Nat:yield start\n"); YIELD(4); printf("Nat:yield end\n"); YIELD(5); } } COEND() };
で、この数値を毎回入れるのが阿呆臭いので、以下のawkスクリプトを噛ませると。
# FIBER BLOCKの開始 /^:blank:*COBEGIN/ { # ([数値])の場所を調べ数値を取得する start = match($0, /\(:blank:*:digit:*:blank:*\)/); end = match($0, /\)/); if (start == end) { totalNumber = 0; } else { totalNumber = strtonum(substr($0, start + 1, end - start - 1)); } fiberID = 1; } # FIBERのYIELD /^:blank:*YIELD/ { # ([数値])の場所を調べ数値を取り除き、新しい数値を入れる start = match($0, /\(:blank:*:digit:*:blank:*\)/); end = match($0, /\)/); print substr($0, 0, start) fiberID substr($0, end); fiberID = fiberID + 1; # YIELDの場合は表示が終わっているので次へ next; } # FIBER BLOCKの終了 /^:blank:*COEND/ { # COBEGINで指定した数より少ない場合にはエラーを表示 if (fiberID - 1 > totalNumber) { printf("error fiber yield too much : %s(%d)\n", FILENAME, NR) > "/dev/stderr"; } } { print $0; }
で、Makefileでこんな風に使えばばっちりと。
$(HOGE_TITLE_SRC:.cpp=.o) : %.o : %.cpp mv $< $<.tmp; awk -f util/fiber.awk $<.tmp > $<; rm $<.tmp $(CXX) $(CFLAG) -Ihoge -Ihoge/title -c -o $@ $<
ただCOBEGINの値はチェックだけして書き変えません。
YIELD()の中の最大値よりも小さいとエラー表示をしますし、実際コンパイル時にエラーになります。