PHPのmatch式に絶望したからネタでC++にmatch式を実装する

## はじめに

※ ネタです

[PHP の match 式](https://www.php.net/manual/ja/control-structures.match.php) を読んでいたら、何やら臭うコードが・・・

```php
function fizzbuzz($num) {
    print match (0) {
        $num % 15 => "FizzBuzz" . PHP_EOL,
        $num % 3  => "Fizz" . PHP_EOL,
        $num % 5  => "Buzz" . PHP_EOL,
        default   => $num . PHP_EOL,
    };
}
```

この `match (0)` 部分、なんか気持ち悪くないですか？

`$num` の値に応じて処理を分岐させたいので、ここでは `match ($num)` と書けるのが美しいと感じました。

この気持ち、C++ にぶつけよう！

## C++

ご存知 C++ には演算子オーバーロードがあり、言語の許す限り任意の構文っぽいものを生み出せます。

これが C++ の `match` 式だ!

case の条件部分に `match (n)` の `n` を引数としたラムダ式を書けるところがポイントです。

```cpp
for (unsigned n = 1; n <= 20; ++n)
{
    auto const s = match (n) [
        (arg % 15 == 0) --> "FizzBuzz",
        (arg %  3 == 0) --> "Fizz",
        (arg %  5 == 0) --> "Buzz",
        default_ --> std::to_string(n)
    ];
    std::cout << s << std::endl;
}
```

<details><summary>コード</summary>

:warning: グローバルに制約のない演算子オーバーロードを配置するなど、汎用性は一切考慮されていないものです

```cpp
#include <iostream>
#include <string>
#include <tuple>

template <typename L, typename R>
struct modulo_expression
{
    L lhs;
    R rhs;

    template <typename A>
    auto eval(A arg) const
    {
        return lhs.eval(arg) % rhs.eval(arg);
    }
};

template <typename L, typename R>
struct equals_expression
{
    L lhs;
    R rhs;

    template <typename A>
    auto eval(A arg) const
    {
        return lhs.eval(arg) == rhs.eval(arg);
    }
};

template <typename T>
struct identity_expression
{
    T value;

    template <typename A>
    T eval(A) const
    {
        return value;
    }
};

struct argument_expression
{
    template <typename A>
    A eval(A arg) const
    {
        return arg;
    }
};

argument_expression const arg;

template <typename L, typename R>
modulo_expression<L, identity_expression<R>> operator % (L lhs, R rhs)
{
    return { lhs, { rhs } };
}

template <typename L, typename R>
equals_expression<L, identity_expression<R>> operator == (L lhs, R rhs)
{
    return { lhs, { rhs } };
}

template <typename E>
struct pattern_expression
{
    E expression;

    template <typename A>
    auto eval(A arg) const
    {
        return expression.eval(arg);
    }
};

template <typename E>
pattern_expression<E> operator -- (E expression, int)
{
    return { expression };
}

template <typename P, typename T, typename N>
struct case_expression
{
    P pattern;
    T value;
    N next;

    template <typename A>
    std::tuple<bool, T> eval(A arg) const
    {
        auto const t = next.eval(arg);
        if (std::get<0>(t))
        {
            return t;
        }
        else
        {
            return std::make_tuple(pattern.eval(arg), value);
        }
    }
};

template <typename P, typename T>
struct case_expression<P, T, void>
{
    P pattern;
    T value;

    template <typename A>
    std::tuple<bool, T> eval(A arg) const
    {
        return std::make_tuple(pattern.eval(arg), value);
    }
};

template <typename P, typename T>
case_expression<P, T, void> operator > (P pattern, T value)
{
    return { pattern, value };
}

template <typename P1, typename T1, typename N1, typename P2, typename T2>
case_expression<P2, T2, case_expression<P1, T1, N1>> operator , (case_expression<P1, T1, N1> lhs, case_expression<P2, T2, void> rhs)
{
    return { rhs.pattern, rhs.value, lhs };
}

struct default_pattern_expression
{
    template <typename A>
    auto eval(A) const
    {
        return true;
    }
};

default_pattern_expression const default_{};

template <typename S>
struct match_expression
{
    S scrutinee;

    template <typename C>
    auto operator [] (C cases) const
    {
        return std::get<1>(cases.eval(scrutinee));
    }
};

template <typename S>
match_expression<S> match(S scrutinee)
{
    return { scrutinee };
}

int main(void)
{
    for (unsigned n = 1; n <= 20; ++n)
    {
        auto const s = match(n) [
            (arg % 15 == 0) --> "FizzBuzz",
            (arg %  3 == 0) --> "Fizz",
            (arg %  5 == 0) --> "Buzz",
            default_ --> std::to_string(n)
        ];
        std::cout << s << std::endl;
    }

    return 0;
}
```

</details>

## おわりに

数年ぶりに書きましたが、やっぱ C++ は楽しいですね〜

今回 `std::to_string` の存在を知りました。便利になりましたね。