平成26年4月30日
C言語でのコルーチン実装
C言語上でコルーチンを実装する方法は幾つかあるようです。その中で比較的単純な実装を示します。
#ifndef COROUTINE_H
#define COROUTINE_H
#define CO_VAL_t int
#define CoStart(p) { \
CO_VAL_t *co_ptr = p; \
switch(*co_ptr) { \
case 0:
#define CoSusp(ret) \
do { \
*co_ptr = __LINE__; \
return ret; \
case __LINE__:; \
} while(0)
#define CoFinish() \
;} \
*co_ptr = 0; \
}
#define CO_reset(co_ptr) *co_ptr = 0
#endif //COROUTINE_H
C言語のマクロ機能を利用した実装です。特に処理の中断位置を記憶するために組み込みマクロ機能__LINE__を巧妙に利用しています。
この実装では
・コルーチンを実装した関数を識別しやすくするために関数の引数として渡されるコルーチン用の変数に型CO_VAL_tを定義
・コルーチンの初期化を明確化するためにCO_reset()マクロを定義
しています。
これだけで複数の処理をマルチタスクで処理できるのですから、そのメリットは絶大です。
CoSusp()にはvoid関数用を別に作る必要があるかもしれません。
PIC18用のXC8コンパイラではvoid型の関数に上記のCoSusp()を使用すると”引数が無い”とワーニングになります。確かにあまりお行儀は良くないので、別に分けた方がいいかもしれません。
#define CoSusp_void() \
do { \
*co_ptr = __LINE__; \
return; \
case __LINE__:; \
} while(0)
タスクコンテキストの切り替えによる実装との比較では、下記が異なります。
・実装は完全にソフトウェアのみで行われているので、タイマのようなハードウェアを必要としません。
・各タスクはタスクの切り替えポイント(CoSusp())に達するまでタスクを切り替え出来ません。
これは並行処理中のタスクの中のどれか一つでも無限ループに落ち込んだだけで全ての処理が停止します。
(これは必ずしもデメリットとは限らないので注意が必要です。状況によってはこの方が望ましいこともあります)
(2021/03/02修正)
コルーチンのキーワード(CoStart, CoSusp, CoFinish)を旧表記(CO_begin, CO_yield, CO_end)から変更した。