## 5.9 挿入子関数の改善

ここでは、挿入子関数の改善方法について検討します。巨大な国別コードテーブルがあって、国別コードへのアクセスに時間がかかる状況を想定します。挿入子関数のパフォーマンスは、国別コードテーブルをキャッシュして直接アクセスするようにし、パフォーマンスのオーバーヘッドをなくすことで最適化することができます。

### 5.9.1 基本キャッシュ

次のコードでは、基本キャッシュを行います。ストリームのロケールオブジェクトから電話ファセットオブジェクトを取り出し、国別コードテーブルを静的変数にコピーします。

>     ostream& operator<<(ostream& os, const phoneNo& pn)
>     {
>        locale loc = os.getloc();
>        const phone_put& ppFacet = use_facet<phone_put> (loc);

>        // 基本キャッシュ
>        static prefixMap_t codes = *(ppFacet.country_codes());

>        // キャッシュコードを使用した高度な出力
>        ...
>        return (os);
>     }

ストリームに組み込まれたロケールオブジェクトは変更されましたが、キャッシュされた静的国別コードテーブルが変更されなかった状況を想定してみます。キャッシュがデータで一杯になると、ストリームのロケールオブジェクトを変更しても、この挿入子関数のキャッシュは変更されません。これは望ましいことではありません。必要な対策は、新しいロケールオブジェクトが組み込まれるたびに何らかの通知を行って、キャッシュを更新することです。

### 5.9.2 コールバック関数の登録

以下の例では、コールバック関数で通知を行います。コールバック関数の登録は入出力ストリームで行います。クラス ios_base で次のように宣言します。

>     enum event { erase_event, imbue_event, copyfmt_event };       //1
>     typedef void (*event_callback) (event, ios_base&, int index);
>     void register_callback (event_callback fn, int index);        //2

//1 | 登録したコールバック関数は、次の 3 つのイベントに呼び出されます。 ストリームの削除 新しいロケールの組み込み ストリーム状態のコピー
//2 | register_callback() 関数では、コールバック関数と、ストリームの parray までのインデックスを登録します。imbue()、copyfmt()、 ~ios_base() に対する呼び出しの間に、関数 fn は引数インデックスとともに呼び出されます。イベントが発生すると、登録された関数は、登録時とは逆の順序で呼び出されます。 parray は、基底クラス ios_base の静的配列です。この配列までのインデックスを使用するには、xalloc() を使用します。また配列にアクセスするには pword(index) か iword(index) を使用します。図 16 にその方法を示します。 #### 図 16 -- 静的配列 parray

キャッシュを更新するコールバック関数をインストールするには、parray までのインデックスを検索してキャッシュを作成し、そのコールバック関数をコンストラクタに登録するクラスを実装します。以下のコードに、その方法を示します。

>     class registerCallback_t {
>     public:
>     registerCallback_t(ostream& os
>                       ,ios_base::event_callback fct
>                       ,prefixMap_t* codes)
>       {
>        int index = os.xalloc();                                   //1
>        os.pword(index) = codes;                                   //2
>        os.register_callback(fct,index);                           //3
>       }
>     };

//1 | 配列までのインデックスを、xalloc() で検索します。
//2 | コードテーブルまでのポインタを pword() で配列に格納します。
//3 | コールバック関数とインデックスを登録します。

parray までのインデックスを使用して、実際のコールバック関数が後でキャッシュにアクセスします。

この段階では、ストリームのロケールを変更するたびにキャッシュを更新するコールバック関数がまだ必要です。以下のコードは、そのようなコールバック関数を示したものです。

>     void cacheCountryCodes(ios_base::event event
>                           ,ios_base& str,int cache)
>     {  if (event == ios_base::imbue_event)                        //1
>        {
>           locale loc = str.getloc();
>           const phone_put<char>& ppFacet =
>                        use_facet<phone_put<char> > (loc);         //2

>           *((phone_put::prefixMap_t*) str.pword(cache)) =
>                        *(ppFacet.country_codes());                //3
>        }
>     }

//1 | イベントが、組み込んだロケールの変更であるかどうかを調べます。
//2 | ストリームのロケールから電話番号ファセットを検索します。
//3 | キャッシュに国別コードテーブルを保存します。キャッシュは、ストリームの parray でアクセスすることができます。

### 5.9.3 挿入子の改善

挿入子に関して、改善すべきところはすべて改善してきました。必要に応じてキャッシュを更新するコールバック関数が挿入子によって登録されます。登録は、クラス registerCallback_t の静的変数の宣言によって 1 回だけ行われます。

>     ostream& operator<<(ostream& os, const phoneNo& pn)
>     {
>      static phone_put::prefixMap_t codes =
>         *(use_facet<phone_put>(os.getloc()).country_codes());     //1

>      static registerCallback_t cache(os,cacheCountryCodes,&codes);//2

>       // some sophisticated output using the cached codes
>       ...
>     }

//1 | 現在の国別コードテーブルがキャッシュされます。
//2 | コールバック関数 cacheCountryCodes が登録されます。

Copyright (c) 1998, Rogue Wave Software, Inc.
このマニュアルに関する誤りのご指摘やご質問は、電子メールにてお送りください。

OEM リリース, 1998 年 6 月