13.11 Custom Instantiation Diagnostics

Ru-Brd

Many templates put some implicit requirements on their parameters. When the arguments of an instantiation of such a template do not fulfill the requirements, either a generic error is issued or the generated instantiation does not function correctly. In early C++ compilers, the generic errors produced during template instantiations were often exceedingly opaque (see page 75 for an example). In more recent compilers, the error messages are sufficiently clear for an experienced programmer to track down a problem quickly, but there is still a desire to improve the situation. Consider the following artificial example ( meant to illustrate what happens in real template libraries):

 template <typename T>  void clear (T const& p)  {      *p = 0;  // assumes  T  is a pointerlike type  }  template <typename T>  void core (T const& p)  {      clear(p);  }  template <typename T>  void middle (typename T::Index p)  {      core(p);  }  template <typename T>  void shell (T const& env)  {      typename T::Index i;      middle<T>(i);  }  class Client {    public:      typedef int Index;   };  Client main_client;  int main()  {      shell(main_client);  } 

This example illustrates the typical layering of software development: High-level function templates like shell() rely on components like middle() , which themselves make use of basic facilities like core() . When we instantiate shell() , all the layers below it also need to be instantiated . In this example, a problem is revealed in the deepest layer: core() is instantiated with type int (from the use of Client::Index in middle() ) and attempts to dereference a value of that type, which is an error. A good generic diagnostic will include a trace of all the layers that led to the problems, but this amount of information may be unwieldy.

An alternative that has often been proposed is to insert a device in the highest level template to inhibit deeper instantiation if known requirements from lower levels are not satisfied. Various attempts have been made to implement such devices in terms of existing C++ constructs (for example, see [BCCL]), but they are not always effective. Hence, it is not surprising that language extensions have been proposed to address the issue. Such an extension could clearly build on top of the static properties facilities discussed earlier. For example, we can envision modifying the shell() template as follows :

 template <typename T>  void shell (T const& env)  {      std::instantiation_error(std::type<T>::has_member_type<"Index">,             "T must have an Index member type");      std::instantiation_error(!std::type<typename T::Index>::dereferencable,             "T::Index must be a pointer-like type");      typename T::Index i;      middle(i);  } 

The instantiation_error() pseudo-function would presumably cause the implementation to abort the instantiation (thereby avoiding the diagnostics triggered by the instantiation of middle() ) and cause the compiler to issue the given message.

Although this is feasible , there are some drawbacks to this approach. For example, it can quickly become cumbersome to describe all the properties of a type in this manner. Some have proposed to allow " dummy code" constructs to serve as the condition to abort instantiation. Here is one of the many proposed forms (this one introduces no new keywords):

 template <typename T>  void shell (T const& env)  {      template try {          typename T::Index p;          *p = 0;      } catch "T::Index must be a pointer-like type";      typename T::Index i;      middle(i);  } 

The idea here is that the body of a template try clause is tentatively instantiated without actually generating object code, and, if an error occurs, the diagnostic that follows is issued. Unfortunately, such a mechanism is hard to implement because even though the generation of code could be inhib-ited, there are other side effects internal to a compiler that are hard to avoid. In other words, this relatively small feature would likely require a considerable reengineering of existing compilation technology.

Most such schemes also have other limitations. For example, many C++ compilers can report diagnostics in different languages (English, German, Japanese, and so forth), but providing various translations in the source code could prove excessive. Furthermore, if the instantiation process is truly aborted and the precondition was not precisely formulated, a programmer might be much worse off than with a generic (albeit unwieldy) diagnostic.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net