Tech Talk

C++ Templates

by Reg. Charney

If you wanted to know just about everything about C++ templates than the book C++ Templates—The Complete Guide by David Vandevoorde and Niccolai Josuttis, (ISBN 0-201-73484-2) is a readable reference book you can use. Normally, discussing a book would appear in a book review. However, since the authors have done such a good job of describing C++ templates, I though the topic and the book deserved more complete coverage. You may recall Niccolai as the author of the excellent text, The C++ Standard Library, ISBN 0-201-37926-0. David is the author of C++ Solutions: Companion to the C++ Programming Language , Third Edition, ISBN 0-201-30965-3. Both authors are also longtime members of the ANSI/ISO C++ X3J16 Standardization Committee.

Ordering Convention

Most of us write the following const or volatile declaration thus:

volatile int vInt;
const char *pcChar;

The authors suggest that a better way is:

int volatile vInt;  // 1
char const *pcChar; // 2

Here, const and volatile appear before what they qualify. Since I read declarations from right to left, in //1, I get vInt is a volatile int and in //2, I get pcChar is a pointer to a const char. The real power comes when we use this ordering in typedef statements. For example,

typedef int  *PINT;
typedef PINT  const CPINT;
typedef const PINT  PCINT;

Here we can see that the first typedef (pointer to int) can be used in the second typedef (const pointer to int). The third typedef completely changes the meaning of the type (pointer to const int).

typename Keyword

Historically, we have used the keyword, class, in template parameter lists:

template< class T > . . .

However, T could be replaced by things other than a class name, so the use of the new keyword typename is preferred.

Reducing Ambiguities

Often, instantiating a template can result in an ambiguity error that is difficult to understand. Here is a simple example:

template< typename T > plot2D(T& x, T& y);

plot2D(3,4);    // ok
plot2D(3.14,1); // err - types of arguments differ

The ambiguity occurs because all parameters are supposed to be of same type, but in the second case, it is unclear whether that type should be an int or a double.

In this simple example (and many others like it), there are 3 ways of disambiguating the statement:

plot2D<int>(3.14,1);               // 3
plot2D(static_cast<int>(3.14), 1); // 4

In //3, the template type is forced to be int. In //4, casting forces all the argument types to be the same. The third way to eliminate this problem uses more sophisticate templates.

Overloading function template

It is possible to have both template and non-template versions of the same function. Often, these non-template versions of the function are called specializations. In such situations, function overloading and its rules are used to resolve any ambiguities. Because we are talking about templates, a few added rules are needed to normal function overloading rules.

Here are some examples:

int cmp(int const& a, int const& b);

template<typename T> T cmp(T const& a, T const &b);

cmp(1,2);        // 5
cmp(4.3, -1.2);  // 6
cmp(‘x’,’s’);    // 7
cmp<>(-3,2);     // 8
cmp<int>(4.3,4); // 9

In //5, the non-template function is the best match (Rule 2). In //6, template argument deduction instantiates the cmp<double> function (Rule 3). In //7, the instantiated function is cmp<char> (Rule 3). In //8, the notation forces use of the template function and results in a cmp<int> function being instantiated and used instead of the non-template version of the cmp function (Rule 4).

Partial Specialization

When a template class or function has more than one template parameter, one or more may be specified creating a partially specialized template. However, if one or more partial specializations match the same template, an ambiguity occurs. For example,

// partials.cpp
#include <typeinfo>
#include <stdio.h>
typedef char const CC;

// general template function
template<typename T1,typename T2>
void f(T1 t1, T2 t2)
{
  CC* s1=typeid(T1).name();
  CC* s2 = typeid(T2).name();
  printf("f(%s,%s)\n",s1,s2);
}

// partially specialization 1
// both type the same
template<typename T>
void f(T t1, T t2)
{
  CC* s1 = typeid(T).name();
  CC* s2 = typeid(T).name();
  printf("f1(%s,%s)\n",s1,s2);
}

// partially specialization 2
// 2nd parameter is non-type
template< typename T>
void f(T t1, int t2)
{
  CC* s1 = typeid(T).name();
  CC* s2 = typeid(int).name();
  printf("f(%s,%s)\n",s1,s2);
}

// partially specialization 3
// parameters are all pointers
template< typename T1, typename T2>
void f(T1* t1, T2* t2)
{
  CC* s1 = typeid(T1*).name();
  CC* s2 = typeid(T2*).name();
  printf("f(%s,%s)\n",s1,s2);
}

int main(int argc,char* argv[])
{
  char const* s1="one";
  f(1,21.3);
  f(1.2,-3);
  f('a','z');
  f(s1,2);
  f(&s1,"two");
  return 0;
}

The output from this program is:

f(int,double)
f(double,int)
f(char,char)
f(CC *,int)
f(CC **,char *)

Note that calling f(3,5) is ambiguous because both f(T,T) and f(T,int) match this call.

Non-type Template Parameters

Both classes and functions can use non-type template parameters. When used, non-type parameters become part of the classes type and functions signature. Thus,

template<typename T, int n>
class C
{
  T a[n];
  // . . .
};

C<char, 10> c10A;
C<char, 5>  c5A;
C<int, 10>  i10A;

Each of c10A, c5A, and i10A are all distinct types.

An example of a template function using non-type parameter follows:

template<typename T, int n>
T g(T t)
{
  return t+n;
}
int main()
{
  printf("g<double,5>(1.3)=%g\n", g<double,5>(1.3));
  printf("g<char,4>('a')=%c\n", g<char,4>('a'));
}

The output of this short program is:

g<double,5>(1.3)=6.3
g<char,4>('a')=e

Non-type template parameters have restrictions: they must be integral values, enumerations, or instance pointers with external linkage. They can’t be string literals nor global pointers since both have internal linkage.

Keyword typename

You may have wondered why we needed the new keyword typename. Besides being a better choice for template parameters, it is needed to disambiguate certain declarations. E.g.,

template<typename T, int n>
class C
{
  typename T::X *p;
  // . . .
};

Without the keyword typename, the declaration for p becomes an expression: the value of T::X is multiplied by the value of p.

Generally, prefix all declarations using template parameters with typename.

this pointer

Normally derived and base classes share the value of this. However, lookup rules for template classes are different. Template base class members are not lookup when searching derived template class member functions.

template<typename T>
class B
{
  void foo();
}

template<typename T>
class D: public B<T>
{
  void bar() { foo(); }
}

In bar(), foo() would not be found. To get B’s foo, you need to say this->foo(). Again, the rule given in the book states that any base class member used in a derived class should be qualified by this-> or B<T>::.

Conclusion

I have not begun to cover many of the interesting aspects of templates in this short article. And I have barely covered the other fun parts of this book. Get it.

Editorial

By Reg. Charney

The Future?

In www.redherring.com December’s newsletter, they listed for 2003 10 predictions from various experts . I was interested to see where it might lead for the Valley. The main predictions are:

If you believe these predictions, you can see that connectiveness is driving where we are going. Bioterrorism is also going to drive more biotech. The expensing of options will reduce their importance in attracting staff.

Reading for the Blind & Dyslexic

I was contact early this month by Luby Aczel, a Volunteer Coordinator for the non-profit Northern California studio of Recording for the Blind and Dyslexic. They create and lend educational material on CD or four track cassette to individuals with learning or visual disabilities. They need volunteers to read the many technical and computer manuals for which they get requests. Please visit their web site at www.rfbd.org to find out more about them. You can also contact Luby Aczel, at 650-493-3717, ext. 13

Book Reviews

Real-Time UML: Developing Efficient Objects for Embedded Systems—2nd Edition by Bruce Powel Douglass, Addison-Wesley, ISBN 0-201-65784-8

Rating: Recommended
Reading Level: Easy.

I believe this book heralds the next step in system design, reliability, and effectiveness. On every page, new ideas jumped out at me.

Real-Time UML is geared towards the beginning to intermediate level. It presents a clear and practical approach for a reader with minimal exposure to UML and embedded systems.

The book is well organized and easy to read. It contains seven chapters. Broadly speaking, the first four chapters focus on analysis and the last three focus on design. Chapter 1 provides an introduction to objects and real-time systems concepts such as concurrency and messaging. Chapter 2 uses UML to analyze real-time systems requirements. Here, use cases are fundamental to capturing the requirements of a system. Chapters 3 and 4 focus on object analysis from two points of view. First, from a structural perspective, it explains how to identify a set of objects that is necessary to realize a set of use cases. Second, from a behavioral point of view, the author explores how do these objects communicate and relate to one another so that the system requirements can be met. Chapters 5, 6 and 7 discuss design topics. Chapter 5 emphasizes architecture, including the use of patterns appropriate for embedded systems. Chapter 6 discusses mechanistic design which involves the organization of classes to support a particular implementation strategy. Chapter 7 dives into detailed design looking at things such as data structure implementation and algorithms.

One of the most interesting topics covered in Real-Time UML is statecharts. Statecharts are a unique modeling tool that allow for better analysis of real time systems. In Chapter 4, the author introduces statecharts and proceeds to give numerous examples of practical applications.

The text is fairly accurate. The use of diagrams and clear examples is widespread throughout. Little code is presented as it is not really needed to introduce UML concepts. However; snippets of C++ can be found where they enhance the explanation of a concept.

The book is very useful for several reasons. It serves as a stepping stone for further study of OO techniques applied to embedded systems. Furthermore; since it covers three broad areas: OO, UML and embedded systems, anyone with more advanced knowledge in any of these areas can use this book to see how the other areas are tied into what they already know.

Bernal A. Arroyo

Trends

By Reg Charney and Ali Çehreli

Windows Weakening?

In this month’s chart, I noticed that Windows development in the Valley seems to be weakening. That is, as a proportion of the total number of job openings offered, the percentage related to Windows has been decreasing, more so than Linux, for example.

Also note that Windows XP is hardly making an impact. There are still more job openings asking for Windows ME skills than for XP knowledge. Also, as a proportion of all the openings advertised, Linux and individual flavors of Windows are about the same.

Another interesting observation is that there was a noticeable up-tick in the jobs offered in August that has subsequently dipped. This also matches the consumer confidence index for the same period.