Apache C++ Standard Library User's Guide

## 26.9 Improving the Inserter Function

Let's turn here to improving our inserter function. Consider that the country code table might be huge, and access to a country code might turn out to be a time-consuming operation. We can optimize the inserter function's performance by caching the country code table, so that we can access it directly and thus reduce performance overhead.

### 26.9.1 Primitive Caching

The code below does some primitive caching. It takes the phone facet object from the stream's locale object and copies the country code table into a static variable.

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

      // primitive caching
      static CodeMap cachedCodes = *(ppFacet.getCodes());

      // some sophisticated output using the cached codes
      ...
      return (os);
    }

Now consider that the locale object imbued on a stream might change, but the cached static country code table does not. The cache is filled once, and all changes to the stream's locale object have no effect on this inserter function's cache. That's probably not what we want. What we do need is some kind of notification each time a new locale object is imbued, so that we can update the cache.

### 26.9.2 Registration of a Callback Function

In the following example, notification is provided by a callback function. The iostreams allow registration of callback functions. Class std::ios_base declares:

    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`

Registered callback functions are called for three events:

- Destruction of a stream

- Imbuing a new locale

- Copying the stream state.

`//2`

The `register_callback()` function registers a callback function and an index to the stream's `parray`. During calls to `imbue()`, `copyfmt()`, or `~ios_base()`, the function `fn``` is called with argument `index`. Functions registered are called when an event occurs, in opposite order of registration. The `parray` is an array member defined in class std::ios_base. One can obtain an index to this array via `xalloc()`, and access the array via `pword(index)` or `iword(index)`, as shown in Figure 16:

#### Figure 16: The array parray

In order to install a callback function that updates our cache, we implement a class that retrieves an index to `parray` and creates the cache, then registers the callback function in its constructor. The procedure is shown in the following code:

    class CallbackRegistrar {
      public:
        CallbackRegistrar(std::ostream& os,
                          std::ios::event_callback fct,
                          CodeMap* cachedCodes)
        {
          int index = os.xalloc();                             //1
          os.pword(index) = cachedCodes;                       //2
          os.register_callback(fct,index);                     //3
        }
    };

`//1` | An index to the array is obtained via `xalloc()`.
`//2` | The pointer to the code table is stored in the array via`pword()`.
`//3` | The callback function and the index are registered.

The actual callback function will later have access to the cache via the index to `parray`. At this point, we still need a callback function that updates the cache each time the stream's locale is replaced. Such a callback function could look like this:

    void cacheCodesCallback(std::ios_base::event callbackEvent,
                            std::ios_base& stream, int cacheIndex)
    {
      if (callbackEvent == std::ios::imbue_event) {             //1
        const phone_put& ppFacet =
          std::use_facet<phone_put> (stream.getloc);            //2
        phone_put::CodeMap* cachedCodes =
          (phone_put::CodeMap*) stream.pword(cacheIndex);       //3

        *cachedCodes = *ppFacet.getCodes();
      }
    }

`//1` | Check whether the event was a change of the imbued locale,
`//2` | retrieve the phone number facet from the stream's locale, and
`//3` | store the country code table in the cache. The cache is accessible via the stream's `parray`.

### 26.9.3 Improving the Inserter

We now have everything we need to improve our inserter. It registers a callback function that updates the cache whenever necessary. Registration is done only once, by declaring a static variable of class `CallbackRegistrar`.

    std::ostream& operator<<(std::ostream& os, const phoneNumber& pn)
    {
     static phone_put::CodeMap codes =
        *(std::use_facet<phone_put>(os.getloc()).getCodes());     //1

     static CallbackRegistrar
         registrar(os,cacheCountryCodes,&codes);                  //2

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

`//1` | The current country code table is cached.
`//2` | The callback function `cacheCodesCallback()` is registered.