静的配列の情報は静的なものなのですからconstexprで受け取りたいですね
// for native array
template < class Ty, std::size_t size > constexpr
std::size_t lengthof( Ty(&)[size] ) noexcept
{ return size; }
// for std::array
template < class Ty, std::size_t size > constexpr
std::size_t lengthof( const std::array < Ty, size >& arg ) noexcept
{ return size; }
int main()
{
int something[16] = {};
lengthof( something ); // 16
std::array < int, 32 > something = {};
lengthof( something ); // 32
}
ふむふむ、なるほど。
いずれも型推論によって簡単に情報を手に入れることができました。
しかしこうしてみると、配列にはサイズ以外にも多くの情報があることがわかります。
基本型や、多次元配列になればそれぞれの次元のサイズ、次元数が挙げられますね。
よって、いくつでも戻り値を返すことができるメタ関数でそれぞれの情報を取り出してみましょう。
先に、次元数を手に入れるメタ関数を定義します。
これは、std::rankというものがあるのですが、標準にも拘らずstd::arrayの次元数を求めることができません。
// std::array < int, 64 >[16]
こんな配列を作る人はまさかいないでしょう。ですから、std::arrayの配列は
// std::array < std::array < int, 64 >, 16 >
のような形になるはずです。
よって、大した手間でもないのでこれに対応したメタ関数を作ってしまいます。
//
// Get Rank of Array
// impl
template < class Ty, std::size_t rank >
struct _rank // Ty is no longer array
{
enum { value = rank };
};
template < class Ty, std::size_t size, std::size_t rank >
struct _rank < std::array < Ty, size >, rank >
{
enum { value = _rank < Ty, rank + 1 >::value };
};
template < class Ty, std::size_t size, std::size_t rank >
struct _rank < Ty[size], rank >
{
enum { value = _rank < Ty, rank + 1 >::value };
};
//
// interface
template < class _array >
using rank = _rank < _array, 0 >;
ややこしく見えますが、配列が配列でなくなるまでバラして、バラすことのできた回数をカウントしています。
もちろん、数えるための引数は見せられないのでエイリアステンプレートにくるんでいます。
これから作るものもこれと同じ方法で作成することになります。
まず、ネイティブの配列からです。
//
// Get Information of Static Array
template < class, std::size_t = 1 >
struct array_element
{ // Ty is not array or
// N is larger than dimension
};
//
// for native array
template < class Ty, std::size_t size >
struct array_element< Ty[size], 1 >
{
using type = Ty[size];
using value_type = Ty;
enum : std::size_t
{
max_size = size,
rank = rank < Ty[size] >::value
};
};
template < class Ty, std::size_t size, std::size_t N >
struct array_element< Ty[size], N >
{
using type = typename array_element< Ty, N - 1 >::type;
using value_type = typename array_element< Ty, N - 1 >::value_type;
enum : std::size_t
{
max_size = array_element< Ty, N - 1 >::max_size,
rank = array_element< Ty, N - 1 >::rank;
};
};
インターフェースは、array_element < Ty, N >
です。
Tyには任意の配列を、Nには取得したい配列の次元を入力します。
例えば、array_element < int[10][20][30], 1 >
とした場合、
type == int[10][20][30],
value_type == int[20][30],
max_size == 10,
rank == 3
となります。
全くやり口は変わらないのですが、std::arrayバージョンです。
//
// for std::array
template < class Ty, std::size_t size >
struct array_element< std::array< Ty, size >, 1 >
{
using type = std::array < Ty, size >;
using value_type = Ty;
enum : std::size_t
{
max_size = size,
rank = rank < std::array < Ty, size > >::value
};
};
template < class Ty, std::size_t size, std::size_t N >
struct array_element< std::array < Ty, size >, N >
{
using type = typename array_element< Ty, N - 1 >::type;
using value_type = typename array_element< Ty, N - 1 >::value_type;
enum : std::size_t
{
max_size = array_element< Ty, N - 1 >::max_size,
rank = array_element< Ty, N - 1 >::rank
};
};
これらは同じ構造体の特殊化なので、全く同じように扱うことができます。
利用するときは、関数のラッパを使うと型推論も働いていい感じでしょう。
//
// get max_size
template < class Ty > constexpr
std::size_t lengthof( const Ty& ) noexcept
{
return array_element < Ty >::max_size;
}
//
// get rank
template < class Ty > constexpr
std::size_t rank( const Ty& ) noexcept
{
return array_element < Ty >::rank;
}; // 尤もこれは、上のrankをそのまま使っても良い
どうにも全体がごちゃごちゃしてしまって美しくありませんが、これで配列の情報を全て(たぶん)得ることができたといえるののではないでしょうか。
rankに限らず、それぞれの機能を分割してarray_elementで統合するようにするべきでしたかね。
このようなものはどこにでもあるようなものとは思いますが、何かのお役に立てばと思います。