C++ による BREW 開発(6)
BREW 開発者は、仮想テーブルの恩恵にあずかっている。
例えば、IAStreamVtbl, IMemAStream, IFileVtbl の定義を見てみよう。
struct IAStreamVtbl{ uint32 (*AddRef) ( IAStream* po ); uint32 (*Release) ( IAStream* po ); void (*Readable)( IAStream* po , PFNNOTIFY pfn , void* pUser ); int32 (*Read) ( IAStream* po , void* pDest , uint32 nWant ); void (*Cancel) ( IAStream* po , PFNNOTIFY pfn , void * pUser ); }; struct IMemAStreamVtbl{ uint32 (*AddRef) ( IMemAStream* po ); uint32 (*Release) ( IMemAStream* po ); void (*Readable)( IMemAStream* po , PFNNOTIFY pfn , void* pUser ); int32 (*Read) ( IMemAStream* po , void* pDest , uint32 nWant ); void (*Cancel) ( IMemAStream* po , PFNNOTIFY pfn , void * pUser ); void (*Set) ( IMemAStream* po , byte* pBuff , uint32 dwSize , uint32 dwOffset , boolean bSysMem ); void (*SetEx) ( IMemAStream* po , byte* pBuff , uint32 dwSize , uint32 nOffset , PFNNOTIFY pUserFreeFn , void* pUserFeeData ); }; struct IFileVtbl{ uint32 (*AddRef) ( IFile* po ); uint32 (*Release) ( IFile* po ); void (*Readable)( IFile* po , PFNNOTIFY pfn , void* pUser ); int32 (*Read) ( IFile* po , void* pDest , uint32 nWant ); void (*Cancel) ( IFile* po , PFNNOTIFY pfn , void * pUser ); uint32 (*Write) ( IFile* pIFile , const void * pBuffer , uint32 dwCount ); int (*GetInfo) ( IFile* pIFile , FileInfo* pInfo ); int32 (*Seek) ( IFile* pIFile , FileSeekType seek , int32 position ); int (*Truncate) ( IFile* pIFile , uint32 truncate_pos ); int (*GetInfoEx) ( IFile* po , AEEFileInfoEx * pi ); int32 (*SetCacheSize)( IFile* po , int nSize ); };
注目すべき点は、IMemAStreamVtbl と IFileVtbl の先頭の5つの関数へのポインタが、IAStreamVtbl と同じということだ。BREW インターフェースオブジェクトを作った人は、こうなるように意図的に仮想テーブルの配置を決めているのだ。
これが何を意味するかというと、
IFILE_Read( pIFile , buf , size ); IASTREAM_Read( pIFile , buf , size );
この2つのマクロ呼び出しは、同じ関数を呼び出すことになるということだ。
また、
IASTREAM_Read( pIMemAStream , buf , size ); IASTREAM_Read( pFile , buf , size );
これは、異なる関数を呼び出すことになる。
これにどんな利点があるかというと、例えば IUNZIPASTREAM_SetStream() の定義は次のようになっている。
void IUNZIPASTREAM_SetStream( IUnzipAStream* pIUnzipAStream , IAStream* pInIAStream );
2番目の引数に、IAStream へのポインタを渡す必要がある。
この IUNZIPASTREAM_SetStream() の内部ではきっと、
IASTREAM_Read( pInIAStream , buf , size );
とか、そういう記述が書かれているに違いない。
ということは、この IAStream へのポインタというのは、仮想テーブルの配置が同じ IMemAStream や IFile でも構わないということなのだ。
pIMemAStream を渡しても、IASTREAM_Read() は IMEMASTREAM_Read() と同じ意味なので大丈夫だろうし、pIFile を渡しても、IASTREAM_Read() は IFILE_Read() と同じ意味なので大丈夫だろう。
IASTREAM_Read() は、中の関数がどんな実装になっているかは知らないけれども、とにかく buf の中に size バイト入れてくれる関数なのだ。
それがメモリからなのかファイルからなのか、それとも外部メモリからなのかネットワーク上なのか知らないが、とにかく buf の中に size バイトセットする関数なのだ。
本来であれば、メモリ上から読み込んで unzip する IUNZIPASTREAM_SetMemory() やら、ファイル上から読み込む IUNZIPSTREAM_SetFile() やらを作ったりする必要があるのだけれども、これだと IUNZIPASTREAM_SetStream() だけで出来るようになるのだ。