配列の情報を取得する　その２

前に投稿した記事、[配列の情報を取得する](http://qiita.com/rinse_/items/9e7b7104321a2a8ee0a5)は気の向くままに書いたため汚いクラスを書いてしまいましたので、少し綺麗にしたものを投稿しようと思います。

```
//
// get array-information
```

配列の基本型を得るメタ関数です。
例えば、`int[5]`の基本型は`int`、`int[10][20]`の基本型は`int[20]`です。
特殊化が前提なのでもっとも基本的な形は空のクラスですが、
SFINAEを考慮して定義だけは書いたほうが良いかと思います。
（SFINAEはsubstitution faultを無視するだけで、定義自体がないとその時点でコンパイルエラーを吐いてしまう。はず。間違ってたらごめんなさい。）

```
// Get Base Type of Array
template < class >
struct array_base
{ // Ty must be array
};
template < class Ty, std::size_t size >
struct array_base < Ty[size] >
{
  using type = Ty;
};
template < class Ty, std::size_t size >
struct array_base < std::array < Ty, size > >
{
  using type = Ty;
};
template < class Ty >
using array_base_t = typename array_base < Ty >::type;
```

同様に、配列の長さを取得するメタ関数です。

```
//
// Get Length of Array
template < class >
struct array_length
{ // Ty must be array
};
template < class Ty, std::size_t size >
struct array_length < Ty[size] >
{
  enum : std::size_t { value = size };
};
template < class Ty, std::size_t size >
struct array_length < std::array < Ty, size > >
{
  enum : std::size_t { value = size };
};
```

これだけは前回作っていました、rankです。
次元数を求めます。
_array_rankは実装を担っています。
引数を使って次元数を数えているので、それを隠すためにインターフェースと分ける必要がありました。

```
//
// Get Rank of Array
template < class, std::size_t rank >
struct _array_rank
{
  enum : std::size_t { value = rank };
};
template < class Ty, std::size_t size, std::size_t rank >
struct _array_rank < std::array < Ty, size >, rank >
{
  enum { value = _array_rank < Ty, rank + 1 >::value };
};
template < class Ty, std::size_t size, std::size_t rank >
struct _array_rank < Ty[size], rank >
{
  enum { value = _array_rank < Ty, rank + 1 >::value };
};
template < class _array >
using array_rank = _array_rank < _array, 0 >;
```

残念ながらもう一つメタ関数。is_arrayです。
std::is_arrayはstd::arrayを配列と認識しません。仲が悪いのかしら。

```
//
// is_array
template < class >
struct is_array : std::false_type {};
template < class Ty, std::size_t size >
struct is_array < Ty[size] > : std::true_type {};
template < class Ty, std::size_t size >
struct is_array < std::array < Ty, size > > : std::true_type {};
```

さて、これらを束ねたarray_elementです。
実装を全て上のメタ関数に投げているので、このクラスはネイティブな配列とSTLとで区別する必要がなくなりました。

```
//
// Get Information of Static Array
template < class Ty, std::size_t N = 1 >
struct array_element
{
  static_assert ( is_array < Ty >::value, "Type must be array" );
  using type = typename array_element < array_base_t < Ty >, N - 1 >::type;
  using value_type = typename array_element < array_base_t < Ty >, N - 1 >::value_type;
  enum : std::size_t 
  {
    max_size = array_element< array_base_t < Ty >, N - 1 >::max_size,
    rank = array_element< array_base_t < Ty >, N - 1 >::rank,
  };
};
template < class Ty >
struct array_element < Ty, 1 >
{ 
  static_assert ( is_array < Ty >::value, "Type must be array" );
  using type = Ty;
  using value_type = array_base_t < Ty >;
  enum : std::size_t
  {
    max_size = array_length < Ty >::value,
    rank = array_rank < Ty >::value,
  };
};
template < class Ty >
struct array_element < Ty, 0 >
{ // N == 0 is invalid
// ここはstatic_assertの方がいいかも？
};
```

値を得るときは型推論の働く関数が良いでしょう。

```
// get length of static array
template < class Ty > static constexpr
std::size_t lengthof( const Ty& ) noexcept
{
  return array_length< Ty >::value;
}
// get rank of static array
template < class Ty > static constexpr
std::size_t rankof( const Ty& ) noexcept
{
  return arrat_rank< Ty >::value;
}
```

オマケです。
std::arrayのbegin() / end()がconstexprじゃないのは悲しいことでございます。
そこで、チョロっと書いてみました。

```
namespace std
{
template < class Ty, std::size_t size > static constexpr
auto begin( std::array < Ty, size >& arg ) -> decltype( arg.begin() )
{
  return reinterpret_cast < decltype( arg.begin() ) > ( &arg );
}
template < class Ty, std::size_t size > static constexpr
auto cbegin( const std::array < Ty, size >& arg ) -> decltype( arg.cbegin() )
{
  return reinterpret_cast < decltype( arg.cbegin() ) > ( &arg );
}

template < class Ty, std::size_t size > static constexpr
auto end( std::array < Ty, size >& arg ) -> decltype( arg.end() )
{
  return reinterpret_cast < decltype( arg.end() ) > ( reinterpret_cast < Ty( & )[size] > ( arg ) + size );
}
template < class Ty, std::size_t size > static constexpr
auto cend( const std::array < Ty, size >& arg ) -> decltype( arg.cend() )
{
  return reinterpret_cast < decltype( arg.cend() ) > ( reinterpret_cast < Ty( & )[size] > ( arg ) + size );
}
}
```

脳内コンパイラではこれでいけた。
それにしてもstd名前空間にSTLであるstd::arrayの特殊化を書くのはあまり褒められたことではないですね。
おとなしくC++14を待ちます。