Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 20.  Shims


20.2. Attribute Shims

Definition: Attribute Shims

Attribute shims retrieve attributes or states of instances of the types for which they are defined.

The names of attribute shims take the form get_xxx, where xxx represents the particular attribute being accessed (except where they form part of a composite shim concept).

The values returned from attribute shims are always valid outside the instance of the shim, in cases where the shim is implemented by the creation of a temporary object.


Consider an illustrative example. The get_ptr attribute shim is a suite of functions that retrieve the pointer attributes of the types to which they are applied. We may define some of its members as follows:

Listing 20.1.


template <typename T>


inline T *get_ptr(T *p)


{


  return p;


}


template <typename T>


inline T *get_ptr(std::auto_ptr<T> &p)


{


  return p.get();


}


template <typename T>


inline T const *get_ptr(std::auto_ptr<T> const &p)


{


  return p.get();


}


template <typename T>


inline T *get_ptr(comstl::interface_ptr<T> &p)


{


  return p.get_interface_ptr();


}



The get_ptr shim allows us to generalize our use of pointers. Consider the scenario where we are writing a functor to work with a sequence of pointers to instances of Resource (or rather of its polymorphic derived classes) in order to preprocess them by a visual system before rendering. Our functor might look like this:

Listing 20.2.


struct resource_prerenderer


  : public std::unary_function<Resource *, void>


{


  resource_prerenderer(VisualSystem *vs)


    : m_vs(vs)


  {}


  void operator ()(Resource *resource)


  {


    m_vs->PreRender(resource);


  }


private:


  Resource  *m_vs;


};



The visible resources can be stored in a VisibleList instance via raw pointer (perhaps in a vector<Resource*>), because their lifetimes are controlled separately in the ResourceManager singleton [Gamm1995], and the contents of the VisibleList instance are vouched by the documented semantics of the class (see section 5.1). This means that the VisibleList can be very efficient, as our requirements dictate. We may now effect prerendering of all visible objects in the following fashion:



std::for_each( visible.begin(), visible.end()


             , resource_prerenderer(g_vs));



However, the system is now to be ported to another architecture, and the Resource instance lifetimes are no longer to be centrally managed. On the new architecture, VisibleList will store them as reference-counting instances using, say, some class Resource_ptr. What should we do? Create a separate functor to handle reference-counted objects, selecting the correct one via the preprocessor? Maintain a single resource_prerender functor but conditionally compile different operator ()() methods? What if the specifications change again? It all sounds like far too much work for this little pixie!

The answer is that we rewrite resource_prerenderer just once,[4] and then we never have to change it again (at least not in respect of changes to the pointer type). It would now look like this:

[4] I'm with James L. Brooks [Broo1995] on this one. You will always write two versions, so don't try to do the ultimate generic version in the first.



template <typename R>


struct resource_prerenderer


{


  . . .


  void operator ()(R &resource)


  {


    m_vs->PreRender(get_ptr(resource));


  }


  . . .



Given the available definitions of get_ptr shown earlier, our code will work whether Resource instances are stored as raw pointers or reference-counted pointers. It would even work if they were stored as std::auto_ptrs, but I know that you know not to try to place auto_ptr in standard library containers [Meye1998, Dewh2003].


      Previous section   Next section