toge's diary

コンピュータ関連の趣味をつらつらと。

もちっと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()の中の最大値よりも小さいとエラー表示をしますし、実際コンパイル時にエラーになります。