C++ Templates solved: How to split the class declaration and implementation into separate files

Templates Background

Templating is one of the major enhancements added to the C++ programming language, extending the capabilities of the original C programming language.  In order to best understand their power, considder the following situation:

Assume that we’re making a sorting function, using the bubblesort function.

1
2
3
bool bubbleSort(int& array){
...
}

Ignoring the implementation of the function for now, take a look at the declaration. Now this function will work perfectly if we want to only sort data of type Integer, but what if we want to say sort chars?

1
2
3
4
5
6
7
bool bubbleSort_int(int& array){
...
}
 
bool bubbleSort_char(char& array){
...
}

The problem here is excess code. Having two identical implementations of the same algorithm, with the only difference being teh data type is very redundant. A better solution would be for us to be able to supply the type we want to use at runtime. This is the power of templates.

1
2
3
4
template<typename T>
bool bubbleSort(T& array){
...
}

In the code above, we specify the variable T to be a type, that we can then define the type to use in the function call simply as follows:

1
bubbleSort<int>(foo);

Thus the problem is then fully solved.

Multiple Files

Traditionally when using C++, we keep a class definition in the .h/.hpp file and the class implementation in the .c/.cpp file. This causes a problem when using class templates. A template strictly speaking is not a class or a function, it is a design pattern. due to this, it needs to be all in one place. If you try to separate the definition and implementation, a linker error will follow.

This creates a problem , as in an application, we will have normal classes with their implementations and declarations split, then any templated classes in a single file. There are several ways around this problem.

The first is simple, provide a prototype for each templated function with each embedded type as follows:

1
template <int> void foo();

This is very cumbersome to say the least, clearly a more generic solution is needed.

Solution 1 : Export Keyword

The C++ “export” keyword was designed to solve this problem. However due to politics, it is seldom implemented by any of the main compillers, and thus is almost useless. The only compiller i could find that implements it was Comeau C++.

Although not often implemented, it provides simple usage, by simply placing the following line in the .c/c.pp implementation file as follows:

1
export template ....

Solution 2 : Preprocessor Macros

Clearly the best way to solve the problem is to use preprocessor macros. However we will also add in support for the export keyword if it is avaliable with the compiller we are using.

First in out declaration (h/.hpp file) we will place the following directives:

1
2
3
4
#ifndef COMPILER_SUPPORTS_EXPORT
#define FROM_MYCLASS_H
#include "myclass.cpp"
#endif

We will use the constant COMPILER_SUPPORTS_EXPORT to define whether the compiler currently being used supports the export keyword. By default, it is not defined, and so we use a preprocessor directoves method. However if we using a compiler that does implement it, we can simply add

1
#define COMPILER_SUPPORTS_EXPORT

somewhere in a global position.

The code (previous) does two jobs. Firstly it checks to see if export is enabled. If not it defines a constant specific to the file, called FROM_MYCLASS_H. We will use this later in the implementation (.c/.cpp file). Finally it includes the implementation file. This will remove all linker error messages.

The next problem is one of redeclaration. If we leave the code as it is, we’re likely to get compiller errors for redeclaration of methods in our class. To solve this, we can add preprocessor directoves in our implementation (.cpp / .c) file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifdef COMPILER_SUPPORTS_EXPORT
#define FROM_MYCLASS_H
#endif
 
#ifdef FROM_MYCLASS_H
 
template<typename T>myclass::mymethod(){
....
}
 
#undef FROM_MYCLASS_H
#endif
#ifdef COMPILER_SUPPORTS_EXPORT
export template.....
#endif

In order to understand this, lets start with line #9. This means that all of our implementation code will ONLY be visable if FROM_MYCLASS_H is defined. Now, if we have come from the include statement in the header file, this will already be defined as normal, and will work perfectly, and we are done. The only thing to add is line #15. Here we undefine our constant, so that the code will not be included for anything else including it in the future.

Now we have to examine the case whereby the compiller does support export, and thus COMPILER_SUPPORTS_EXPORT is defined. In this case, the source file will not be included by the header file, as we specified earlier. However we need the .cpp file to show its code, and thus FROM_MYCLASS_H must therefore be defined. To solve this problem, we put the code:

1
2
3
#ifdef COMPILER_SUPPORTS_EXPORT
#define FROM_MYCLASS_H
#endif

If the compmiller supports export, we define FROM_MYCLASS_H so that the compiller can see the sourcode when compilling the source (.c/.cpp file) directly, and then remove the constant afterwards as normal.

This gives us final code of:

Header (.h) file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef MYCLASS_H
#define MYCLASS_H
 
template<typename T>
class myclass{
...
};
 
#ifndef COMPILER_SUPPORTS_EXPORT
#define FROM_MYCLASS_H
#include "myclass.cpp"
#endif
 
#endif

Source (.cpp) file:

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifdef COMPILER_SUPPORTS_EXPORT
#define FROM_MYCLASS_H
#endif
 
#ifdef FROM_MYCLASS_H
 
template<typename T>myclass::mymethod(){}
 
#undef FROM_MYCLASS_H
#endif
#ifdef COMPILER_SUPPORTS_EXPORT
export template...
#endif

Happy Coding :)

Share and Enjoy:

  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Mixx
  • Google
  • Blogsvine
  • E-mail this story to a friend!
  • LinkedIn
  • Live
  • NewsVine
  • Print this article!
  • Reddit
  • Slashdot
  • Spurl
  • StumbleUpon
  • Technorati
  • YahooMyWeb

Tags: , , , ,

Leave a Reply