C++11でストロング・ブレーク

この記事は[ADVENTARのC++ Advent Calender 2015](http://www.adventar.org/calendars/926)の18日目の記事です。

ところで、当投稿は[QiitaのC++ Advent Calendar 2015](http://qiita.com/advent-calendar/2015/c_plusplus_programming_language)の記事ではありません。
実は、ADVENTARへ投稿を登録した当記事が思ったより膨らんだため、分割して一部をQiitaのC++カレンダーへ[投稿](http://qiita.com/Chironian/items/643d2e1a25d4f258f3e4)させて頂いております。

#1.はじめに
私は、折角のC++をベターCとして長年使ってましたが、去年、今更ですがC++11規格の存在を知りC++の発展にびっくり。バリバリ使い始めて、多少は役に立ちそうな小技ツールをいくつか作ったので、その中から２つ、ご紹介させて頂きます。
もし、お役に立つことがありましたら、幸いです。
#2.まずは軽くstd::extent<>用マクロ
C++11で規格された多次元生配列の要素数を取ってくる[std::extent<>](http://www.cplusplus.com/reference/type_traits/extent/)は便利なのですが、ラムダ式の中などで参照になってしまうと使えません。参照を[std::remove_reference<>](http://www.cplusplus.com/reference/type_traits/remove_reference/)で外せば良いのですが、滅多矢鱈長くなるので嫌らしいです。
こんな時は、タイプ量を減らすためにマクロ化です。EXTENT()は１次元用、EXTENT_N()は多次元用です。
使い方は次節を参照下さい。早速ラムダ式の中で使ってます。

```extent.h
#include <type_traits>

#define EXTENT(dType)           \
    std::extent<typename std::remove_reference<decltype(dType)>::type, 0>::value
#define EXTENT_N(dType, dRank)  \
    std::extent<typename std::remove_reference<decltype(dType)>::type, dRank>::value
```

#3.本命、ストロング・ブレーク
多重ループやループの中のswitchからループ脱出に苦労された方はいませんか？　そんなあなたに朗報(?)です。

C++11でサポートされた[ラムダ式](http://kaworu.jpn.org/cpp/%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F)は、return;で呼び出し元に戻ります。そして、ラムダ式は定義しているところから、そのまま直接呼び出すことができます。これを使ってラムダ式をその場実行し、内部の多重ループからreturn;すると、あら不思議、多重ループをブレークできます。

更に戻り値を返却できるので、最後まで回ったら0、ブレークしたら0以外を返却すれば、抜けたところでブレーク処理や非ブレーク処理を記述できます。
ポイントだけ書くと⇩こんな感じです。

if ( int ret = **\[&\]()->int { {ループ{ブレークしたい時return x;} } return 0;}()** )
{
　　ブレーク時の処理;
}
else
{
　　最後まで回った時の処理;
}

太文字部分が[ラムダ式](http://kaworu.jpn.org/cpp/%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F)です。ラムダ式を定義してそのまま実行し、その戻り値をret変数へ設定しています。
「ここでループ」の中で、return x;することによりループやswitchを全て中断し、xをretへ設定します。
このret変数は、then節内でも参照できます。（else節内でももちろん参照できますが、常に0なので意味ないです。）

マクロを使って下記のように書けるようにしてみました。

```cpp
TRY(ret)
{
    ループ
    {
        ブレークしたい時、BREAK(x); // ただし、xはint型でx!=0
    }
}
BROKEN()
{
    ブレーク時の処理; // retにBREAK(x)のxが設定されている。
}
NOT_BROKEN()
{
    最後まで回った時の処理; // retは常に0
}
```
以下、ソースです。有効行はたった5行です。でもこの5行だけだと、確実にハマるのでコメントを付けてます。

```strong_break.h
// ***************************************************************************
//      ストロング・ブレーク
//          使い方例①：
//              TRY(ret)        // 内部でint ret;と宣言されている。
//              {
//                  while(...)
//                  {
//                      :
//                      while(...)
//                      {
//                          :
//                          switch(...)
//                          {
//                              :
//              BREAK(1);       // retに1が設定される。
//                              :
//                          }
//                          :
//                      }
//                      :
//                  }
//              }
//              BROKEN()        // BREAK(x);した。(ただし、x!=0)
//              {
//                  std::cout << "BROKEN(ret=" << ret << ")\n";
//              }
//              NOT_BROKEN()    // ループを最後まで回った(もしくはBREAK(0);した。)
//              {
//                  std::cout << "NOT_BROKEN\n";
//              }
//
//          使い方例②(BROKENのみ)：
//              TRY(ret)        // 内部でint ret;と宣言される。
//              {
//                  :
//              BREAK(2);       // retに2が設定される。
//                  :
//              }
//              BROKEN()        // BREAK(x);した。(ただし、x!=0)
//              {
//                  std::cout << "BROKEN(ret=" << ret << ")\n";
//              }
//
//          使い方例③(COMPLETEDのみ)：
//              TRY(ret)        // 内部でint ret;と宣言される。
//              {
//                  :
//              BREAK(3);       // retに3が設定される。
//                  :
//              }
//              COMPLETED()     // ループを最後まで回った(もしくはBREAK(0);した。)
//              {
//                  std::cout << "COMPLETED()\n";
//              }
// ***************************************************************************

#pragma once

#define TRY(dVar)       if (int dVar=[&]()->int {
#define BREAK(dValue)   return dValue
#define BROKEN()        return 0;}())
#define NOT_BROKEN()    else
#define COMPLETED()     return 0;}()){}else
```
以下使用例です。

```strong_break.cpp
#include <iostream>
using namespace std;

#include "strong_break.h"
#include "extent.h"

int main()
{
    int const values[]={4,2,9,3,6};

    // 単ループから脱出し、ブレークした時に処理
    TRY(ret)
    {
        for (int i=0; i < EXTENT(values); ++i) {
            if (values[i] == 3)
    BREAK(i+1);
        }
    }
    BROKEN()
    {
        std::cout << "BROKEN(ret=" << ret << ")\n";
    }

    // 単ループから脱出し、ブレークした時としなかった時の両方で処理
    TRY(ret)
    {
        for (int i=0; i < EXTENT(values); ++i) {
            if (values[i] == 10)
    BREAK(i+1);
        }
    }
    BROKEN()
    {
        std::cout << "BROKEN(ret=" << ret << ")\n";
    }
    NOT_BROKEN()
    {
        std::cout << "NOT_BROKEN(ret=" << ret << ")\n";
    }

    // 単ループから脱出し、最後まで実行した時(=ブレークしなかった時)に処理
    TRY(ret)
    {
        for (int i=0; i < EXTENT(values); ++i) {
            if (values[i] == 10)
    BREAK(i+1);
        }
    }
    COMPLETED()
    {
        std::cout << "COMPLETED(ret=" << ret << ")\n";
    }

    // 多重ループからの脱出
    int const values2D[][2]={{4,0},{2,6},{9,7},{3,2},{6,1}};
    TRY(ret)
    {
        for (int i=0; i < EXTENT_N(values2D, 0); ++i) {
            for (int j=0; j < EXTENT_N(values2D, 1); ++j) {
                if (values2D[i][j] == 6) {
    BREAK(i+1);
                }
            }
        }
    }
    BROKEN()
    {
        std::cout << "BROKEN(ret=" << ret << ")\n";
    }

    // ループ内のswitchからのループ脱出
    TRY(ret)
    {
        for (int i=0; i < EXTENT(values); ++i) {
            switch(values[i])
            {
            case 0:
                break;
            case 1:
                break;
            case 9:
    BREAK(i+1);
            }
        }
    }
    BROKEN()
    {
        std::cout << "BROKEN(ret=" << ret << ")\n";
    }

    return 0;
}
```
C++11対応が必要です。MSVC 2015とMinGW 4.9.2の-std=c++11オプション付きで動作確認しています。

【閑話休題】
本当は、`TRY {...} CATCH(int e) {...}`みたいに書けるようにしたかったのですが、私の頭ではいくら考えてもできそうになかったので諦めました。
`TRY(int ret)`みたいに「戻り値の型」をユーザが定義するようにもできる筈ですが、使うのが難しくなりそう（boolへの暗黙の型変換が必須）な割にメリットが少ないのでこちらも断念してます。

#4.最後に
正直、かなり意表を突く実装と思います。
勝負ごとなら意表を突くのは良いことですが、プログラミングの世界では重々慎むべきことです。知らない人にとって、非常に迷惑な話ですから。
ですので、業務では使う前に関係者の了解を得ることをお薦めします。
プロジェクトの特性によっては使うべきでないケースもありますので。

因みに、これの簡易版について[teratailで意見を聞いてみた](https://teratail.com/questions/19023)ことがあるのですが、可読性の面から使わないとの回答が大半でした。その後見つけたのですが、[stackoverflowの4年前のQA](http://stackoverflow.com/questions/6077718/is-this-a-valid-abuse-of-lambda-expressions)で直接ラムダ式を使う案について、同様な議論がされてました。回答も大差ありませんでした。

この時より自然な構文でかけるようにできたし、goto使うよりコードを綺麗に保てるので、私のプロジェクトでは使います。