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を実装します。