There are several ways to organize template source code. This
section presents the most popular approach as of the time of this writing: the
inclusion model.
Linker Errors
Most C and C++ programmers organize their nontemplate code
largely as follows:
-
Classes and other types are entirely placed in header files. Typically, this is a file with a .hpp (or .H, .h, .hh, .hxx) filename extension.
-
For global variables and (noninline) functions, only a declaration is put in a header file, and the definition goes into a so-called dot-C file. Typically, this is a file with a .cpp (or .C, .c, .cc, or .hxx) filename extension.
This works well: It makes the needed type definition easily
available throughout the program and avoids duplicate definition errors on
variables and functions from the linker.
With these conventions in mind, a common error about which
beginning template programmers complain is illustrated by the following
(erroneous) little program. As usual for "ordinary code," we declare the
template in a header file:
print_typeof() is the declaration of a simple
auxiliary function that prints some type information. The implementation of the
function is placed in a dot-C file:
The example uses the typeid operator to print a string
that describes the type of the expression passed to it.
Finally, we use the template in another dot-C file, into which
our template declaration is #included:
A C++ compiler will most likely accept this program without any
problems, but the linker will probably report an error, implying that there is
no definition of the function print_typeof().
The reason for this error is that the definition of the
function template print_typeof() has not been instantiated. In order
for a template to be instantiated, the compiler must know which definition
should be instantiated and for what template arguments it should be
instantiated. Unfortunately, in the previous example, these two pieces of
information are in files that are compiled separately. Therefore, when our
compiler sees the call to print_typeof() but has no definition in sight
to instantiate this function for double, it just assumes that such a
definition is provided elsewhere and creates a reference (for the linker to
resolve) to that definition. On the other hand, when the compiler processes the
file myfirst.cpp, it has no indication at that point that it must
instantiate the template definition it contains for specific arguments.
Templates in Header Files
The common solution to the previous problem is to use the same
approach that we would take with macros or with inline functions: We include the
definitions of a template in the header file that declares that template. For
our example, we can do this by adding
at the end of myfirst.hpp or by including
myfirst.cpp in every dot-C file that uses the template. A third way, of
course, is to do away entirely with myfirst.cpp and rewrite
myfirst.hpp so that it contains all template declarations and template definitions:
This way of organizing templates is called the inclusion model. With this in place, you should find
that our program now correctly compiles, links, and executes.
There are a few observations we can make at this point. The
most notable is that this approach has considerably increased the cost of
including the header file myfirst.hpp. In this example, the cost is not
the result of the size of the template definition itself, but the result of the
fact that we must also include the headers used by the definition of our
template—in this case <iostream> and <typeinfo>.
You may find that this amounts to tens of thousands of lines of code because
headers like <iostream> contain similar template definitions.
This is a real problem in practice because it considerably
increases the time needed by the compiler to compile significant programs. We
will therefore examine some possible ways to approach this problem in upcoming
sections. However, real-world programs quickly end up taking hours to compile
and link (we have been involved in situations in which it literally took days to
build a program completely from its source code).
Despite this build-time issue, we do recommend following this
inclusion model to organize your templates when possible. We examine two
alternatives, but in our opinion their engineering deficiencies are more serious
than the build-time issue discussed here. They may have other advantages not
directly related to the engineering aspects of software development,
however.
Another (more subtle) observation about the inclusion approach
is that noninline function templates are distinct from inline functions and
macros in an important way: They are not expanded at the call site. Instead,
when they are instantiated, they create a new copy of a function. Because this
is an automatic process, a compiler could end up creating two copies in two
different files, and some linkers could issue errors when they find two distinct
definitions for the same function. In theory, this should not be a concern of
ours: It is a problem for the C++ compilation system to accommodate. In
practice, things work well most of the time, and we don't need to deal with this
issue at all. For large projects that create their own library of code, however,
problems occasionally show up.
Finally, we need to point out that what applies to the ordinary
function template in our example also applies to member functions and static
data members of class templates, as well as to member function templates.
-----------------------------------------------------------------
See Also:
-----------------------------------------------------------------
See Also:
-----------------------------------------------------------------
- Complete Tutorial of C++ Template's
- Standard Template Library Tutorial
- Inter Process Communication Tutorial
- Advance Programming in C & C++
-----------------------------------------------------------------
No comments:
Post a Comment