gotoキャンペーン協賛マクロ

# はじめに

ニュースに疎いのですが昨今gotoが世間を賑わしているようです。ダイクストラらに批判されてから幾十年，ようやく復権の兆しなのでしょうか。
かくいう私もC/C++でgotoを使用したことが一度[^1]しかなく，いまさらgotoを使おうにも上手く使いこなせる自信がありません。
そこで低迷したgotoの振興を目指し，C言語の制御構文を自動的にgoto文に変換するマクロを開発しました。

[^1]: マイコン用にCで書いていて，例外もフラグも使いたくなかったので。

# 簡単な例

まずは簡単な例としてdo～whileの変換をやってみます。[^2]

[^2]: 本当に簡単な例ですがgotoの文法を忘れていたため**goto LABEL_1:;**と末尾に余計な:を追加してしまい手間取りました。

* ソース

```C++:sample1.c
#if !defined(PP_ONLY)
#   include <stdio.h>
#endif

#define DO          LABEL_1:
#define WHILE(X)    if(X) goto LABEL_1

int main(void)
{
	int i = 0;

	DO {
		printf("%d\n",i);
		i++;
	} WHILE(i<10);

	return 0;
}
```

* 確認方法

正しく変換されていることを確認するため，プリプロセッサを単独で使用します。標準ライブラリのincludeを行うと変換結果が読みにくくなりますが，マクロPP_ONLYをコマンドラインで指定するとincludeを抑制します。
確認をUbuntu 18.04.4(Windows 10 WSL2)上のgcc 7.5.0で行いました。

```Bash:コマンド
cpp -D PP_ONLY -o sample1.i sample1.c
```

* 結果

```C++:sample1.i
int main(void)
{
 int i = 0;

 LABEL_1: {
  printf("%d\n",i);
  i++;
 } if(i<10) goto LABEL_1;

 return 0;
}
```

do～whileからgoto文への置き換えができました。

# 多重ループへの対応

先の例ではgotoの飛び先ラベルが決め打ちになっており，多重ループに対応できません。ネストごとにラベルを変更するため，なんらかのカウンタを導入してラベルに番号をつけるなどの工夫が必要です。
そこでBoostライブラリの[BOOST_PP_SLOT](https://boostjp.github.io/archive/boost_docs/libs/preprocessor/ref/slot.html)を参考にしてプリプロセッサマクロによるカウンタを実装しました。カウンタのテストコードを以下に示します。SET_VALUEに値を入れてPP_VAR.hをincludeするとPP_VARに値が返ります。

* ソース

```C++:sample2.c
#if !defined(PP_ONLY)
#   include <stdio.h>
#endif

int main(void)
{
	#define SET_VALUE 2
	#include "PP_VAR.h"
	printf("count = %d\n",PP_VAR);

	#define SET_VALUE (PP_VAR + 1)
	#include "PP_VAR.h"
	printf("count = %d\n",PP_VAR);

	#define SET_VALUE (PP_VAR + 1)
	#include "PP_VAR.h"
	printf("count = %d\n",PP_VAR);

	#define SET_VALUE (PP_VAR - 1)
	#include "PP_VAR.h"
	printf("count = %d\n",PP_VAR);

	return 0;
}
```

```C++:PP_VAR.h
/* プリプロセッサによる変数，BOOSTライブラリの真似 */

# if !defined(SET_VALUE)
#     error "SET_VALUE not defined"
# endif

# if   (SET_VALUE) == 0
#     undef PP_VAR
#     define PP_VAR 0
# elif (SET_VALUE) == 1
#     undef PP_VAR
#     define PP_VAR 1
# elif (SET_VALUE) == 2
#     undef PP_VAR
#     define PP_VAR 2
# elif (SET_VALUE) == 3
#     undef PP_VAR
#     define PP_VAR 3
# elif (SET_VALUE) == 4
#     undef PP_VAR
#     define PP_VAR 4
# elif (SET_VALUE) == 5
#     undef PP_VAR
#     define PP_VAR 5
# elif (SET_VALUE) == 6
#     undef PP_VAR
#     define PP_VAR 6
# elif (SET_VALUE) == 7
#     undef PP_VAR
#     define PP_VAR 7
# elif (SET_VALUE) == 8
#     undef PP_VAR
#     define PP_VAR 8
# elif (SET_VALUE) == 9
#     undef PP_VAR
#     define PP_VAR 9
# elif (SET_VALUE) == 10
#     undef PP_VAR
#     define PP_VAR 10
# else
#     error "out of range"
# endif

# undef SET_VALUE
```

* 結果

```C++:sample2.i
int main(void)
{

# 1 "PP_VAR.h" 1
# 9 "sample2.c" 2
 printf("count = %d\n",2);


# 1 "PP_VAR.h" 1
# 13 "sample2.c" 2
 printf("count = %d\n",3);


# 1 "PP_VAR.h" 1
# 17 "sample2.c" 2
 printf("count = %d\n",4);


# 1 "PP_VAR.h" 1
# 21 "sample2.c" 2
 printf("count = %d\n",3);

 return 0;
}
```

## 計画の破綻と方針転換

ところがこのカウンタを使って制御構文を実装しようとすると，大きな問題が二つ出てきました。

1. #includeを使っているが，マクロで置換した結果に#includeが含まれていてもパースできない
2. 置換結果に改行を含めることができない

課題1の対策として邪道ですがプリプロセッサを2回通すことにします。しかしその場合でも課題2がいかんともしがたく，C言語の範疇での解決をあきらめてsedを最小限で併用することにしました。

## while発の構文が対象除外に，ネストの深いループも控えて

do～while以外のループですが，forについては**若干**構文を変形し，FOR(初期化式) TO(終了条件式) STEP(増減式) {} NEXT;としました。まるでBASICですね。条件式の真偽がforと逆になっていることに気を付けてください。
whileで始まるwhile(){}についてはキーワードがdo～whileと重複するため，断腸の思いであきらめました。
また，カウンタがBOOSTと異なり手抜き実装のため10段までしかネストできません。
マクロとサンプルのソース，および変換結果を以下に示します。

* ソース

```C++:sample3.c
#if !defined(PP_ONLY)
#   include <stdio.h>
#endif

#include "goto_campaign.h"
PROLOGUE

int main(void)
{
    FOR(int i = 0) TO(i >= 3) STEP(i++) {
        int j = 0;

        DO {
            printf("%d %d\n", i, j);
            j++;
        } WHILE(j < 3);
    } NEXT;

    return 0;
}
```

```C++:goto_campaign.h
#if !defined(GOTO_CAMPAIGN_H)

#if !defined(PASS2)

    #define PROLOGUE                        \
        {CR}#define SET_VALUE 0             \
        {CR}#include "PP_VAR.h"{CR}         \
        {CR}#define PASS2                   \
        {CR}#include "goto_campaign.h"{CR}

    #define INC_LABEL                       \
        {CR}#define SET_VALUE (PP_VAR + 1)  \
        {CR}#include "PP_VAR.h"{CR}

    #define DEC_LABEL                       \
        {CR}#define SET_VALUE (PP_VAR - 1)  \
        {CR}#include "PP_VAR.h"{CR}

    #define DO          \
        INC_LABEL       \
        LABEL(PP_VAR):

    #define WHILE(X)            \
        if(X) GOTO(PP_VAR);     \
        DEC_LABEL

    #define FOR(X) X;

    #define TO(X)                       \
        INC_LABEL                       \
        {CR}if(X) GOTO_NEXT(PP_VAR);    \
        {CR}GOTO_STEP(PP_VAR);          \
        {CR}TO_LABEL(PP_VAR):           \
        {CR}if(X) GOTO_NEXT(PP_VAR);    \
        {CR}GOTO_STEP(PP_VAR);{CR}

    #define STEP(X)                 \
        {CR}LABEL(PP_VAR):          \
        {CR}X;                      \
        {CR}GOTO_TO(PP_VAR);        \
        {CR}STEP_LABEL(PP_VAR):{CR}

    #define NEXT                    \
        {CR}GOTO(PP_VAR);           \
        {CR}NEXT_LABEL(PP_VAR):     \
        DEC_LABEL

#else

    #define LABEL_SUB(X) LABEL_##X
    #define LABEL_SUB2(X,Y) LABEL_##X##_##Y

    #define LABEL(X) LABEL_SUB(X)
    #define TO_LABEL(X) LABEL_SUB2(X,TO)
    #define STEP_LABEL(X) LABEL_SUB2(X,STEP)
    #define NEXT_LABEL(X) LABEL_SUB2(X,NEXT)

    #define GOTO(X) goto LABEL(X)
    #define GOTO_TO(X) goto TO_LABEL(X)
    #define GOTO_STEP(X) goto STEP_LABEL(X)
    #define GOTO_NEXT(X) goto NEXT_LABEL(X)

#endif

#endif
```


* 確認方法

```Bash:コマンド
# 変換結果の確認
cpp  -D PP_ONLY sample3.c | sed -e s/{CR}/\\n/g | cpp -o sample3.i
# コンパイル
cpp sample3.c | sed -e s/{CR}/\\n/g | cpp -o sample3.i && gcc -o sample3 sample3.i
```

* 結果

(見やすいように整形してあります)

```C++:sample3.i
int main(void)
{
    int i = 0;

    if (i >= 3) goto LABEL_1_NEXT;
    goto LABEL_1_STEP;
LABEL_1_TO:
    if (i >= 3) goto LABEL_1_NEXT;
    goto LABEL_1_STEP;

LABEL_1:
    i++;
    goto LABEL_1_TO;
LABEL_1_STEP:
    {
        int j = 0;

    LABEL_2:
        {
            printf("%d %d\n", i, j);
            j++;
        }
        if (j < 3) goto LABEL_2;
        ;
    }

    goto LABEL_1;
LABEL_1_NEXT:
    ;

    return 0;
}
```

# 終わりに

変換結果のスパゲッティぶりがgoto濫用らしくて趣深いかと思います。
不要不急のネタ投稿ですが，記事中のすべてのコードに対して一応CC0を宣言しておきます。
[![CC0](http://i.creativecommons.org/p/zero/1.0/88x31.png "CC0")](http://creativecommons.org/publicdomain/zero/1.0/deed.ja)