variantの実装(2) -aligned_storageを作る-

前回(variantの実装(1) -みんな大好きテンプレートメタプログラミング!- - while( c++ );)のコメントで、アライメントの問題に関してご指摘を頂いたので、variantの実装の前にaligned_storageを実装してみたいと思います。
ただしVC++2008だけを想定した手抜き実装です。

今回の目標

template< std::size_t size_, std::size_t alignment_ >
struct aligned_storage
{
    union data_t
    {
        char buffer[ size_ ];
        typename type_with_alignment< alignment_ >::type align;
    }data;
};
    //サイズ==8、アライメント==4のストレージ
    typedef aligned_storage< 8, 4 > storage_t;
    std::cout
        << sizeof( storage_t ) << std::endl                     //8
        << alignment_of< storage_t >::value << std::endl        //4
        ;
    //アライメントが2の乗数以外の時はstatic_assertでコンパイルエラーにする
    typedef aligned_storage< 8, 3 > storage_t;

以上のような記述ができるように、

  • alignment_of
  • aligned_storage
  • type_with_alignment
  • integral_constant
  • static_assert

を作ります。

alignment_of

型Tのアライメントを求めるメタ関数。

使い方
size_t a = alignment_of<T>::value;
実装

std::tr1の実装を参考にしました。というかパクリですね。

template< typename T >
struct get_align_
{
    T t1;
    char c;
    T t2;
};
#define get_align( T ) ( sizeof( get_align_< T > ) - 2 * sizeof( T ) )

template< typename T >
struct alignment_of
    : integral_constant< size_t, get_align( T ) >
{};

//referenceのアライメントはpointerとして扱う
template< typename T >
struct alignment_of< T& >
    : integral_constant< size_t, get_align( T* ) >
{};

boostではalignment_of_hackとして以下のように実装されています。ちょっとだけ改変。

template <typename T>
struct alignment_of_hack
{
    char c;
    T t;
    alignment_of_hack();
};

template <typename T>
struct alignment_of
    : integral_constant< size_t, sizeof(::boost::detail::alignment_of_hack<T>) - sizeof(T) >
{};

type_with_alignment

指定したアライメントの型を返すメタ関数。
この実装のでかなり悩んだのですが、結局以下のようなVC++限定の手抜き実装にしました^^
アライメントは2の乗数のみ指定できます。

実装
template< std::size_t Align >
struct type_with_alignment_impl
{
    //とりあえずAlignが2の乗数以外の時はコンパイルエラーにする
    static_assert( ( Align > 0 ) && ( ( Align & ( Align - 1 ) ) == 0 ) );
};

template< std::size_t Align >
struct type_with_alignment
    : type_with_alignment_impl< Align >{};

struct                              a1{      __int8 m; };
struct                              a2{     __int16 m; };
struct                              a4{     __int32 m; };
struct                              a8{     __int64 m; };
struct __declspec( align(  16 ) )  a16{ char m[  16 ]; };
struct __declspec( align(  32 ) )  a32{ char m[  32 ]; };
struct __declspec( align(  64 ) )  a64{ char m[  64 ]; };
struct __declspec( align( 128 ) ) a128{ char m[ 128 ]; };

template<> struct type_with_alignment<   1 >{ typedef   a1 type; };
template<> struct type_with_alignment<   2 >{ typedef   a2 type; };
template<> struct type_with_alignment<   4 >{ typedef   a4 type; };
template<> struct type_with_alignment<   8 >{ typedef   a8 type; };
template<> struct type_with_alignment<  16 >{ typedef  a16 type; };
template<> struct type_with_alignment<  32 >{ typedef  a32 type; };
template<> struct type_with_alignment<  64 >{ typedef  a64 type; };
template<> struct type_with_alignment< 128 >{ typedef a128 type; };

static_assert

コンパイルタイムassert。
条件がfalseであれば、static_assert_failureが未定義となり、以下のようなコンパイルエラーになる。

error C2027: 認識できない型 'static_assert_failure<b>' が使われています。
実装
template< bool b >struct static_assert_failure;
template<>struct static_assert_failure< true >{ enum{ value = 1 }; };

template< int x >struct static_assert_test{};

//compile time assert
#define static_assert( b ) \
    typedef static_assert_test \
        < sizeof( static_assert_failure< ( bool )(b) > ) > \
        static_assert_typedef;

aligned_storage

char buffer[ size_ ]とtype_with_alignmentのunionを用意すれば、bufferのアライメントを指定できる。

実装
//
//size_       bufferのサイズを指定
//alignment_  アライメントを指定
//
template< std::size_t size_, std::size_t alignment_ >
struct aligned_storage
{
    union data_t
    {
        char buffer[ size_ ];
        
        //alignment_に対応する型
        //  1   __int8
        //  2   __int16
        //  4   __int32
        //  8   __int64
        typename type_with_alignment< alignment_ >::type align;
    }data;

    void* address()const{ return const_cast< aligned_storage* >( this ); }
};

使ってみる

    typedef aligned_storage< 8, 4 > storage_t;
    std::cout
        << sizeof( storage_t ) << std::endl                     //8
        << alignment_of< storage_t >::value << std::endl        //4
        ;


次回はaligned_storageを使ってvariantを実装します。