SUMMARY
Occasionally, bugs are found in a template library. Because
all the source code for the library is available, it is tempting to go into the
source code and modify the library. This is not advisable because there may be
unintended side effects from the modifications to the library. In addition,
technical support staff from the library vendor may decline to support a
modified library.
This article describes the following methods to
work around bugs in template libraries without modifying the library, by using
features of the C++ language:
- Specialize the template function that has the
bug.
- Specialize the template class member that has the
bug.
- Specialize the template class that has the bug.
- Derive from the template class to extend its
functionality.
- Use a #define/#undef pair around a #include to change a
symbol name in a template header that may be causing a conflict.
MORE INFORMATION
Below are several descriptions of how to work around bugs
in a template library. One or more of these workarounds may be applicable in
any given case.
The first three methods discuss specializing a
template. To discover all of the specializations required, compile without
optimizations (to prevent inlining any of these functions). Then either use
DUMPBIN /SYMBOLS or use the
/MAP linker option to generate a map file. In either case, you can
find all of the specializations of the template class, function, or member used
in the application. You need to provide a separate template specialization for
each of these.
Method 1. Specialize the template function that has the bug.
To work around a bug in the template function CopyElements in
AfxTempl.h that causes a series of elements to be incorrectly copied, you can
specialize CopyElements for each type used in your program. The template
declaration in AfxTempl.h is:
template<class TYPE> inline void AFXAPI
CopyElements(TYPE* pDest, const TYPE* pSrc, int nCount)
{
...
}
Supply a specialization for each object type that is used in
CopyElements. For example, suppose you use CopyElements for an array of class
MyType. Then your specialization would look similar to the following:
template<>
inline void AFXAPI
CopyElements<MyType>(MyType* pDest, const MyType* pSrc, int nCount)
{
// You supply the code to copy the elements.
}
Method 2. Specialize the template class member that has the bug.
To work around a bug in the template class member function
deque<T>::_Buyback(), you can specialize this member function for each
type of deque in your program. For instance, if you used a deque<MyType>
in your program, you can specialize _Buyback() as follows:
template<>
void std::deque<MyType>::_Buyback()
{
// You supply the code.
}
Method 3. Specialize the template class that has the bug.
It is usually simpler to specialize class members that contain
bugs. However, there are some circumstances when the whole class must be
specialized. For instance, Visual C++ 5.0 doesn't support partial template
specialization. This means that class iterator_traits<T*> does not exist,
which means you can't use iterator_traits with a pointer type. In this case,
you can specialize iterator_traits for each of the pointer types in your
program:
namespace std {
template<>
struct iterator_traits<MyType*> {
typedef ptrdiff_t difference_type;
typedef MyType value_type;
typedef MyType* pointer;
typedef MyType& reference;
typedef random_access_iterator_tag iterator_category;
};
}
Method 4. Derive from the template class to extend its functionality.
Suppose that you need class CComPtr (defined in Atlbase.h) to
have additional members const operator ->>() const and operator const
T*() const. To accomplish this, create a template class and derive from it, and
provide the additional member functions you need:
template <class T>
class CMyComPtr : public CComPtr<T>
{
public:
operator const T*() const {return (const T*)p;}
const T* operator->() const {_ASSERTE(p!=NULL);return (const T*)p;}
// You supply the other required member functions.
};
Your derived class should mimic the behavior of the base class as
closely as possible, with the exception of the added members, to extend the
functionality of the base class. The derived class does not need to supply an
implementation of every member of the base class. But pay careful attention to
construction and copy (assignment operators and copy constructors) so that your
derived class can be used in the same ways as the base class.
Method 5. Use a #define/#undef pair around a #include to change a symbol
name in a template header that may be causing a conflict.
For instance, suppose you had a source file MyProg.cpp:
#include <vector>
class MyTest {
public:
int _Ty;
bool operator < (const MyTest&) const;
bool operator == (const MyTest&) const;
};
int main ()
{
std::vector<MyTest> a;
return 1;
}
This results in the following error in Visual C++ 5.0:
error C2300: 'MyTest' : class does not have a destructor
called '~Ty'
This is clearly a bug. The cause of the bug is that the
template parameter "class _Ty" in one of the STL headers conflicts with the
data member named "_Ty" in class MyTest. To work around the problem, you need
to change one of the symbols--either "class _Ty" or "int _Ty". Renaming "int
_Ty" in MyTest is the preferred way to work around this problem. If this is not
possible, as a last resort you can change the template parameter "class _Ty" to
"class _Ty0" without modifying the STL library. This can be done by surrounding
the include directive for the vector header with #define/#undef directives as
follows:
#define _Ty _Ty0
#include <vector>
#undef _Ty