melt日記

.NETすらまともに扱えないへたれのページ

ホーム 連絡をする 同期する ( RSS 2.0 ) Login
投稿数  111  : 記事  3  : コメント  8241  : トラックバック  41

ニュース

わんくま同盟

わんくま同盟

C# と VB.NET の質問掲示板

iKnow!


Dictation



書庫

C++ の template は非常に便利なのですが、型毎にクラスが作られるため、バイナリサイズが増えてしまいます。

大体はそんなのは無視することが出来るのでしょうけど、本当に容量の厳しい組み込み系のアプリ(というかBREWアプリ)の場合、そうもいきません。

 

昔、template をガシガシ使った BREW のアプリを作ったのですが、大体バイナリサイズは 400KB ぐらいになりました。

その後 template を使わずに同じような規模のアプリを作ってみると、バイナリサイズは 200KB ぐらいでした。

単純計算で2倍の違いが出ています。

300KB までのアプリしか入らないという制限がある端末があったとすれば、template を使わずに作った 200KB のアプリは入っても、template を使って作った 400KB のアプリは入らないことになります。

 

そこで選択肢としては、

1.300KB 制限の端末を切り捨てる

2.template を使わずに書く

というのがあるわけですが、template を使わずに書くことによる作業効率の低下と、300KB 制限を切り捨てることによるシェアの低下を天秤に掛けることになります。

 

しかし、何とか template を使ってもバイナリサイズが小さくなるような方法は無いのでしょうか。

 

なぜバイナリサイズが大きくなるかというと、型毎にクラスが生成されてしまうからです。

同じような処理で、型だけ違うようなメソッドを幾つも作っているわけです。

言い換えれば、バイナリのコード密度が低いということです。

 

例えば vector の場合、処理全てを template にする必要はなく、最低限、型のサイズ・コピーコンストラクタ・デストラクタ・代入演算子(operator=)・比較演算子(operator==, operator<, operator>)があれば、あとは共通の処理で書くことが出来ます。

以下のようなコードになります。

// 型毎に必要な処理
class vector_proxy{
protected:
    // コピーコンストラクタ
    virtual void _copy_constructor( void** _P , const void*& _X ) = 0;
    // デストラクタ
    virtual void _destructor( void** _P ) = 0;
    // operator=
    virtual void _object_equal( void*& lhs , const void*& rhs ) = 0;
    // operator==
    virtual bool _object_eqeq( const void*& lhs , const void*& rhs ) = 0;
    // operator <
    virtual bool _object_lt( const void*& lhs , const void*& rhs ) = 0;
    
    void** _First;
    void** _Last;
    void** _End;
    
    friend class vector_helper;
};

// vector の共通の処理を集めたクラス
class vector_helper{
private:
    // void&が使えないので、void*&を使う。そのため、_Tyをvoid*と定義する。
    typedef void*           _Ty;
    typedef vector_proxy    _Myt;
    
    typedef size_t          size_type;
    typedef ptrdiff_t       difference_type;
    typedef _Ty*            _Tptr;
    typedef const _Ty*      _Ctptr;
    typedef _Ty&            reference;
    typedef const _Ty&      const_reference;
    typedef _Ty             value_type;
    
    typedef _Tptr           iterator;
    typedef _Ctptr          const_iterator;
    
// iterator, const_iteratorの、byte単位の加減算
#define iter_plus( p , n )      ((iterator)((byte*)(p) + (n)))
#define iter_minus( p , n )     ((iterator)((byte*)(p) - (n)))
#define c_iter_plus( p , n )    ((const_iterator)((byte*)(p) + (n)))
#define c_iter_minus( p , n )   ((const_iterator)((byte*)(p) - (n)))
    
public:
    
    static void constructor( _Myt& v , size_type n , const _Myt& _X ){
        v._First = _allocate( _size( _X ) );
        v._Last = _Ucopy( v , n , _X._First , _X._Last , v._First );
        v._End = v._Last;
    }
    static void destructor( _Myt& v , size_type n ){
        _Destroy( v , n , v._First , v._Last );
        _deallocate( v._First );
        v._First = 0;
        v._Last = 0;
        v._End = 0;
    }
    
    ...
    
private:
    static void _Destroy( _Myt& v , size_type n , iterator _F , iterator _L ){
        for( ; _F != _L ; _F = iter_plus( _F , n ) ){
            v._destructor( _F );
        }
    }
    static void _Ufill( _Myt& v , size_type n , iterator _F , size_type _N , const _Ty &_X ){
        for( ; 0 < _N ; --_N , _F = iter_plus( _F , n ) ){
            v._copy_constructor( _F , *(const void**)&_X );
        }
    }
    
    static iterator _allocate( size_type n , size_type _N ){
        return (iterator)operator new( n * _N );
    }
    static void _deallocate( iterator _F ){
        operator delete( _F );
    }
};

// これが vector 本体。protected 継承にしておく。
template< class _Ty >
class vector : protected vector_proxy{
public:
    typedef vector< _Ty > _Myt;
    
    typedef size_t          size_type;
    typedef ptrdiff_t       difference_type;
    typedef _Ty*            _Tptr;
    typedef const _Ty*      _Ctptr;
    typedef _Ty&            reference;
    typedef const _Ty&      const_reference;
    typedef _Ty             value_type;
    
    typedef _Tptr           iterator;
    typedef _Ctptr          const_iterator;
    
    vector( const _Myt& _X )
        { vector_helper::constructor( *this , _sizeof() , _X ); }
    ~vector()
        { vector_helper::destructor( *this , _sizeof() ); }
    
    // vector のメソッドが省略されているけど、
    // どれも vector_helper に委譲しているだけ。
    ...
    
protected:
    // コピーコンストラクタ
    virtual void _copy_constructor( void** _P , const void*& _X )
        { new( (void*)_P ) _Ty( *(_Ty*)&_X ); }
    // デストラクタ
    virtual void _destructor( void** _P )
        { ((_Ty*)_P)->~_Ty(); }
    // operator=
    virtual void _object_equal( void*& lhs , const void*& rhs )
        { (*(_Ty*)&lhs) = (*(const _Ty*)&rhs); }
    // operator==
    virtual bool _object_eqeq( const void*& lhs , const void*& rhs )
        { return ((*(_Ty*)&lhs) == (*(_Ty*)&rhs)); }
    // operator <
    virtual bool _object_lt( const void*& lhs , const void*& rhs )
        { return ((*(_Ty*)&lhs) < (*(_Ty*)&rhs)); }
    // sizeof( _Ty )
    size_type _sizeof()
        { return sizeof( _Ty ); }
};

長くてすんませんこれでも減らした方なんです(;´Д`)

注目すべきは vector クラスで、vector クラスのメソッド全てが、1行で定義されていることです。

本来の処理を、非template な vector_helper に委譲することによって、vector テンプレートが増えても容量がほとんど増えなくなります。

 

こうやって、最低限必要な部分だけ取りだして、それ以外の処理を全て非 template に委譲してやれば、コード密度が高くなり、容量をほとんど増やすことなく、template を使用することが出来るようになります。

 

厳しい容量の中でどうしても template が使いたい場合にどうぞー(そして誰もいなくn(ry

投稿日時 : 2007年1月19日 21:21

コメント

# re: template によるバイナリの肥大化を防ぐ 2007/01/20 2:48 επιστημη
なるほどーん

Post Feedback

タイトル
名前
Url:
コメント