class BulletHolder
{
private :
float x, y ;
public :
virtual void update() = 0 ;
virtual BulletHolder * construct_copy(void * ptr) const = 0 ;
virtual ~BulletHolder() { }
} ;
// BulletHolder *型のあとに構築されるオブジェクトの
// アラインメントを合わせるために必要なパディング値を返す
template < typename T >
struct get_offset
: boost::mpl::eval_if_c<
sizeof(BulletHolder *) < boost::alignment_of<T>::value
, boost::mpl::int_< boost::alignment_of<T>::value - sizeof(BulletHolder *) >
, boost::mpl::int_< 0 >
>
{ } ;
#define BULLET_MAKE_CONSTRUCT_COPY_PP(TYPE) \
virtual BulletHolder * construct_copy(void * ptr) const \
{ return new( static_cast<void *>(static_cast<char *>(ptr) + get_offset<TYPE>::value) ) TYPE(*this) ; }
class Aligned_4_Bullet : public BulletHolder
{
private :
boost::aligned_storage<4, 4>::type padding ; // 4バイトアライン
public :
BULLET_MAKE_CONSTRUCT_COPY_PP(Aligned_4_Bullet)
virtual void update()
{ std::cout << "4 byte aligned " << std::endl ; }
virtual ~Aligned_4_Bullet() { }
} ;
class Aligned_8_Bullet : public BulletHolder
{
private :
boost::aligned_storage<8, 8>::type padding ; // 8バイトアライン
public :
BULLET_MAKE_CONSTRUCT_COPY_PP(Aligned_8_Bullet)
virtual void update()
{ std::cout << "8 byte aligned" << std::endl ; }
virtual ~Aligned_8_Bullet() { }
} ;
#undef BULLET_MAKE_CONSTRUCT_COPY_PP
class Bullet
{
private :
BulletHolder * ptr ;
static size_t const size = 32 ;
char buf[size] ; //BulletHolderを継承するすべてのクラスが収まるサイズ
private :
template < typename T >
void * getAlignedptr()
{
return static_cast< void * >( &buf[ get_offset<T>::value ] ) ;
}
void * getbufptr()
{
return static_cast< void * >( &buf[ 0 ] ) ;
}
void safe_destruct()
{
if ( ptr != 0 )
ptr->~BulletHolder() ;
}
public :
Bullet() : ptr(0) { }
template < typename T >
Bullet ( T const & x )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<BulletHolder, T>::value )) ;
BOOST_STATIC_ASSERT(( sizeof(T) + get_offset<T>::value <= size )) ;
ptr = new( getAlignedptr<T>() ) T(x) ;
}
template < typename T >
Bullet & operator = ( T const & x )
{
BOOST_STATIC_ASSERT(( boost::is_base_of<BulletHolder, T>::value )) ;
BOOST_STATIC_ASSERT(( sizeof(T) + get_offset<T>::value <= size )) ;
safe_destruct() ;
ptr = new( getAlignedptr<T>() ) T(x) ;
}
Bullet( Bullet const & b )
{
if (b.get() != 0)
ptr = b.get()->construct_copy( getbufptr() ) ;
}
Bullet & operator = ( Bullet const & b )
{
if ( this != &b && b.get() != 0)
{
safe_destruct() ;
ptr = b.get()->construct_copy( getbufptr() ) ;
}
return *this ;
}
BulletHolder * get() const
{ return ptr ; }
BulletHolder * operator *()
{ return get() ; }
BulletHolder * operator ->()
{ return get() ; }
~Bullet()
{ safe_destruct() ; }
// デバッグ用
void debug()
{
printf("&buf[0] == %p ptr = %p\n", getbufptr(), ptr ) ;
}
} ;
int main()
{
Aligned_4_Bullet a4 ;
Aligned_8_Bullet a8 ;
Bullet b4(a4) ;
Bullet b8(a8) ;
b4.debug() ;
b8.debug() ;
}
重要な部分は、get_offsetメタ関数だ。必要なパディング数を返してくれる。しかもすばらしいことに、このコードを利用する人は、これらの実装を一切気にしなくてもよいということだ。弾を作るにしても、 BulletHolderを継承して、BULLET_MAKE_CONSTRUCT_COPY_PPという、おまじないマクロを使っておけばよい。
上記のコードでは、たとえばポインタのサイズが4バイトだったとして、もし、3バイトアラインメントなどという変態アラインメントがあった場合は、問題なのだが、そもそもpower of 2以外のアラインメントなどありえるのだろうか。
ためしに、Boostのalignment_storageで、3バイトや5バイトのアラインメントを試してみたが、STATIC_ASSERTに引っかかる。Boostがダメと言っているのだから、たぶんそんな変態アラインメントのハードウェアは、存在しないのだろう。
しかし、つくづくテンプレートメタプログラミングは面白いと思う。get_offsetのようなコードを書くと、まさにコンパイラでメタプログラミングしているんだという実感が沸いてくる。
No comments:
Post a Comment