自分なりのscoped enumビットフラグヘルパー

ビットフラグとsocped enumあたりを調べたら、

- [C++のscoped enumで関数のフラグ指定をしたい](https://qiita.com/prickle/items/c4de8cc23556c6a3d93b) by @prickle 
- [君の名は・・・enum class](https://qiita.com/yumetodo/items/6f7b5c3714b821fe0196) by @yumetodo
- [Re2: C++のscoped enumで関数のフラグ指定をしたい & 君の名は・・・enum class](https://qiita.com/akinomyoga/items/a5df34fc78efd86fa4ef) by @akinomyoga

というのを見かけて、自分なりに作りたくなったので作りました。
今回、説明はかなりザックリです。

(2018/08/04追記)
さらなる別解が投稿されてたのでここでも紹介します。

- [enum class 同士の計算を僕も実装してみた](https://qiita.com/leico/items/d137399e9b926678af44) by @leico


# 方針

　今回はできるだけシンプルにしたいので1つのenumを対象にします（2つ以上のenumを受け入れるようにするのが面倒なので）。

　上記の記事で特定の構造体を特殊化して演算子を使えるようにする時、`std::true_type`を継承する必要があったので、継承する必要がないようにします。つまり、以下のように`enable_bit_flags_operators`を特殊化することで演算子が使えるようにします。

```c++:定義例
// 例えばこういうビットフラグのenumを定義
enum struct flags_t
{
    a = 1,
    b = 1 << 1,
};

// flags_tについて演算子が使えるように有効化する
namespace spirea {

    template <>
    struct enable_bit_flags_operators< flags_t >
    { };

}
```
```c++:使用例
// ビット演算子が使えるようになる
flags_t v = flags_t::a | flags_t::b
```
こんな感じで書けるようにしたいと思います。
ちなみに、`spirea`は適当に作ってる俺々ライブラリの名前です。

# コード

こうなりました。一応解説は下の方に。

```c++:bit_flags.hpp
#ifndef SPIREA_BIT_FLAGS_HPP_
#define SPIREA_BIT_FLAGS_HPP_

#include <type_traits>

namespace spirea {
	
	template <typename T>
	struct enable_bit_flags_operators;

	template <typename T, typename = void>
	struct is_enabled_bit_flags_operators :
		public std::false_type
	{ };

	template <typename T>
	struct is_enabled_bit_flags_operators< T, std::void_t< decltype( enable_bit_flags_operators< T >() ) > > :
		public std::true_type
	{ 
		static_assert( std::is_enum_v< T > );
	};

	template <typename T>
	constexpr bool is_enabled_bit_flags_operators_v = is_enabled_bit_flags_operators< T >::value;

namespace detail {

	template <typename T>
	constexpr auto operator~(T x) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T >
	{
		return static_cast< T >( ~static_cast< std::underlying_type_t< T > >( x ) );
	}

	template <typename T>
	constexpr auto operator&(T lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T >
	{ 
		using underlying = std::underlying_type_t< T >;
		return static_cast< T >( static_cast< underlying >( lhs ) & static_cast< underlying >( rhs ) );
	}

	template <typename T>
	constexpr auto operator|(T lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T >
	{ 
		using underlying = std::underlying_type_t< T >;
		return static_cast< T >( static_cast< underlying >( lhs ) | static_cast< underlying >( rhs ) );
	}

	template <typename T>
	constexpr auto operator^(T lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T >
	{ 
		using underlying = std::underlying_type_t< T >;
		return static_cast< T >( static_cast< underlying >( lhs ) ^ static_cast< underlying >( rhs ) );
	}

	template <typename T>
	constexpr auto operator&=(T& lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T& >
	{
		return lhs = lhs & rhs;
	}

	template <typename T>
	constexpr auto operator|=(T& lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T& >
	{
		return lhs = lhs | rhs;
	}

	template <typename T>
	constexpr auto operator^=(T& lhs, T rhs) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T& >
	{
		return lhs = lhs ^ rhs;
	}

} // namespace detail

} // namespace spirea

using spirea::detail::operator~;
using spirea::detail::operator&;
using spirea::detail::operator|;
using spirea::detail::operator^;
using spirea::detail::operator&=;
using spirea::detail::operator|=;
using spirea::detail::operator^=;

namespace spirea {

	template <typename T>
	constexpr auto enabled(T src, T v) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, bool >
	{
		return ( src & v ) == v;
	}

	template <typename T>
	constexpr auto disabled(T src, T v) noexcept
		-> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, bool >
	{
		return !enabled( src, v );
	}

} // namespace spirea

#endif // SPIREA_BIT_FLAGS_HPP_
```

# 解説

## `is_enabled_bit_flags_operators`を使ったオーバーロード

　重要なのは`is_enabled_bit_flags_operators`メタ関数です。これは`enable_bit_flags_operators`が`T`で特殊化されている場合`std::true_type`、いない場合を`std::false_type`で返すものです。以下にこのメタ関数を抜粋します。

```c++
    template <typename T, typename = void>
    struct is_enabled_bit_flags_operators :
        public std::false_type
    { };

    template <typename T>
    struct is_enabled_bit_flags_operators< T, std::void_t< decltype( enable_bit_flags_operators< T >() ) > > :
        public std::true_type
    { 
        static_assert( std::is_enum_v< T > );
    };

    template <typename T>
    constexpr bool is_enabled_bit_flags_operators_v = is_enabled_bit_flags_operators< T >::value;
```

実装としては、`is_enabled_bit_flags_operators`を特殊化で`enable_bit_flags_operators`のオブジェクトを作ってみてSFINAEで場合分けをしています。
　
　ちなみに`std::void_t`はC++17で追加されたもので、何をいくつテンプレート引数に入れてもvoidを返すメタ関数です。上記のように特殊化でSFINAEするときに便利です。
> std::void_t - cppreference.com
> http://en.cppreference.com/w/cpp/types/void_t

　構造体の`is_enabled_bit_flags_operators`はそのままだと使いにくいので`is_enabled_bit_flags_operators_v`を書いて楽をします。あとは、`is_enabled_bit_flags_operators`を使って演算子を`std::enable_if_t`でオーバーロードしていくだけです。以下は`operator|`を抜粋したものです。

```c++
    template <typename T>
    constexpr auto operator|(T lhs, T rhs) noexcept
        -> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, T >
    { 
        using underlying = std::underlying_type_t< T >;
        return static_cast< T >( static_cast< underlying >( lhs ) | static_cast< underlying >( rhs ) );
    }
```
返り値の型を後置にしているのはただの趣味です。

## enabled関数、disabled関数

フラグが立っているか判定するのに`( src & v ) == v`の`v`みたいに同じフラグを2回書くのが面倒だなーと思ったので作った関数です。特に説明いらないですよね。

```c++
    template <typename T>
    constexpr auto enabled(T src, T v) noexcept
        -> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, bool >
    {
        return ( src & v ) == v;
    }

    template <typename T>
    constexpr auto disabled(T src, T v) noexcept
        -> std::enable_if_t< is_enabled_bit_flags_operators_v< T >, bool >
    {
        return !enabled( src, v );
    }
```

# おわりに

こういう例もあるよってことで1つ。

# 参考
std::void_t - cppreference.com
http://en.cppreference.com/w/cpp/types/void_t

spirea/bit_flags.hpp
https://github.com/LNSEAB/spirea/blob/master/include/spirea/bit_flags.hpp