Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Introspecting Function Templates

The one nested element which the TTI library does not introspect is function templates.

Function templates, like functions, can be member function templates or static member function templates. In this respect they are related to functions. Function templates represent a family of possible functions. In this respect they are similar to class templates, which represent a family of possible class types.

The technique for introspecting class templates in the TTI library is taken from the implementation of the technique in the Boost MPL library. In the case of BOOST_TTI_HAS_TEMPLATE it directly uses the Boost MPL library functionality while in the case of BOOST_TTI_HAS_TEMPLATE_CHECK_PARAMS it replicates much of the technique in the Boost MPL library. The technique depends directly on the fact that in C++ we can pass a template as a parameter to another template using what is called a "template template" parameter type.

One obvious thing about a template template parameter type is that it is a class template. For whatever historical or technical reasons, no one has ever proposed that C++ have a way of passing a function template directly as a template parameter, perhaps to be called a "function template template" parameter type. I personally think this would be a good addition to C++ and would make the ability of passing a template as a parameter to another template more orthogonal, since both class templates and function templates would be supported. My efforts to discuss this on the major C++ newsgroups have met with arguments both against its practical usage and the justification that one can pass a function template to another template nested in a non-template class, which serves as a type. But of course we can do the same thing with class templates, which is in fact what Boost MPL does to pass templates as metadata, yet we still have template template parameters as class templates.

Nonetheless the fact that we can pass class templates as a template parameter but not function templates as a template parameter is the major factor why there is no really good method for introspecting function templates at compile time.

Instantiating a nested function template

There is, however, an alternate but less certain way of introspecting a function template. I will endeavor to explain why this way is not currently included in the TTI library, but first I will explain what it is.

It is possible to check whether some particular instantiation of a nested function template exists at compile-time without generating a compiler error. Although checking if some particular instantiation of a nested function template exists at compile-time does not prove that the nested function template itself does or does not exist, since the instantiation itself may be incorrect and fail even when the nested function template exists, it provides a partial, if flawed, means of checking.

The code to do this for member function templates looks like this ( similar code also exists for static member function templates ):

template
  <
  class C,
  class T
  >
struct TestFunctionTemplate
  {
  typedef char Bad;
  struct Good { char x[2]; };
  template<T> struct helper;
  template<class U> static Good check(helper<&U::template SomeFuncTemplateName<int,long,double> > *);
  template<class U> static Bad check(...);
  static const bool value=sizeof(check<C>(0))==sizeof(Good);
  };

where 'SomeFuncTemplateName' is the name of the nested function template, followed by some parameters to instantiate it. The 'class C' is the type of the enclosing class and the 'class T' is the type of the instantiated member function template as a member function.

As an example if we had:

struct AType
  {
  template<class X,class Y,class Z> double SomeFuncTemplateName(X,Y *,Z &) { return 0.0; }
  };

then instantiating the above template with:

TestFunctionTemplate
  <
  AType,
  double (AType::*)(int,long *,double &)
  >

would provide a compile-time boolean value which would tell us whether the nested member function template exists for the particular instantiation provided above. Furthermore, through the use of a macro, the TTI library could provide the means for specifying the name of the nested member function template ('SomeFuncTemplateName' above) and its set of instantiated parameters ('int,long,double' above) for generating the template.

So why does not the TTI library not provide at least this much functionality for introspecting member function templates, even if it represents a partially flawed way of doing so ?

The reason is stunningly disappointing. Although the above code is perfectly correct C++ code ( 'clang' works correctly ), two of the major C++ compilers, in all of their different releases, can not handle the above code correctly. Both gcc ( g++ ) and Visual C++ incorrectly choose the wrong 'check' function even when the correct 'check' function applies ( Comeau C++ also fails but I am less concerned about that compiler since it is not used nearly as much as the other two ). All my attempts at alternatives to the above code have also failed. The problems with both compilers, in this regard, can be seen more easily with this snippet:

struct AType
 {
 template<class AA> void SomeFuncTemplate() { }
 };

template<class T>
struct Test
 {
 template<T> struct helper;
 template<class U> static void check(helper<&U::template SomeFuncTemplate<int> > *) { }
 };

int main()
  {
  Test< void (AType::*)() >::check<AType>(0);
  return 0;
  }

Both compilers report compile errors with this perfectly correct code,

gcc:

error: no matching function for call to 'Test<void (AType::*)()>::check(int)'

and msvc:

error C2770: invalid explicit template argument(s) for 'void Test<T>::check(Test<T>::helper<&U::SomeFuncTemplate<int>> *)'

There is a workaround for these compiler problems, which is to hardcode the name of the enclosing class, via a macro, in the generated template rather than pass it as a template type. In that case both compilers can handle both the member function code and the code snippet above correctly. In essence, when the line:

template<class U> static void check(helper<&U::template SomeFuncTemplate<int> > *) { }

gets replaced by:

template<class U> static void check(helper<&AType::template SomeFuncTemplate<int> > *) { }

both gcc and Visual C++ work correctly. The same goes for the 'check' line in the 'TestFunctionTemplate' above.

But the workaround destroys one of the basic tenets of the TTI library, which is that the enclosing class be passed as a template parameter, especially as the enclosing class need not actually exist ( see BOOST_TTI_MEMBER_TYPE and the previous discussion of 'Nested Types' ), without producing a compiler error. So I have decided not to implement even this methodology to introspect nested function templates in the TTI library.


PrevUpHomeNext