Topic : STL Newbie Guide
Author : Mumit Khan
Page : << Previous 9  Next >>
Go to page :


code generated, and the other one that is that even you don't use sort on a list, you still have to define relation operator <.
Manual instantiation is not uniform across platforms. The Utah HP PA GCC release needs some extra instantiations.
Some non-standard methods (like erase()) and non-standard algorithms (like release). The documentation does mention that these are non-standard, but the index should note that as well. Both are trivial to write, and I did to make sure that the code works with other implementations.
Good and bad:
OS provides a <stl.h> that includes EVERYTHING. I personally liked it when I started out, and I recommend the same to newcomers. After you figure out what STL is all about, it's pretty easy to include only the necessary include files. Obvious compile time tradeoff. This may not be available in other implementations (eg., ones from HP and FSF/GNU), but again, trivial to write. See here for an example one.
Some header file names may be different from other implementations. For example, OS <algorith.h> vs FSF/GNU and HP <algo.h>. If you choose to include individual headers instead of the <stl.h> shabang, watch out for these tiny things if you need portability with other implementations.
overall, it's definitely worth the money.

Template instantiation with GCC
If you have templated code (eg., when using STL) and you're stuck with GCC, there are three ``somewhat portable'' ways to go (I don't like weird #pragma directives that are compiler specific):

Compile everything with no special flags and let GCC instantiate everything in sight statically each translation unit. This is great if you're building small applications (or if you have a single source file), but results in incredible code bloat for medium to large applications.
Compile everything with -fno-implicit-templates and manually instantiate the templates. Takes a bit of patience (and a C++ demangler, such as c++filt), but certainly doable and it does alleviate the template bloat somewhat. See here for more info.
If you're willing to upgrade to GCC version 2.7.0 and apply the frepo patch from Jason Merrill of Cygnus Support, you will be pleasantly surprised at how easy template instantiation can be. See here for more info.
Until I upgraded to GCC 2.7.0, I had been using the first method when starting a new project and then migrate to the second one. Now with the repository mechanism, I hardly ever do any manual instantiation.

Visibility of template definitions
One major complication with GCC is that all the template definitions must be visible at the point of instantiation (which may not be the case with Borland, but I wouldn't know about that), and if you have the template class members defined in a .cc file that is not included in the declaration file (.h), then you'll get undefined errors. The simple trick there is to conditionally include the .cc file in the template header .h file with some preprocessor directive that gets set for compilers like gcc. Take the following example:


=== template.h
template <class T>
class XX {
public:
    XX(T t) : t_(t) { }
    T value() const;  // declaration only, defn not inline.
private:
    T t_;
};

==== template.cc
#include "template.h"

template <class T>
T XX<T>::value() const {
    return t_;
}

=== main.cc
#include <iostream.h>
#include "template.h"

int main (int, char*[]) {
    XX<int> xx(5);
    cerr << "value = " << xx.value() << endl;
    return 0;
}

===


This will *NOT* work for gcc unless you also include template.cc at the end of template.h. You'll get undefined error that looks something like:

    
    ld: Undefined symbol
 XX<int>::value(void) const
    collect2: ld returned 2 exit status


To get around this, you can always do the following:


=== template.h
template <class T>
class XX {
public:
    XX(T t) : t_(t) { }
    T value() const;
private:
    T t_;
};

#if INCLUDE_TEMPLATE_DEFINITION
# include "template.cc"
#endif

===


and somewhere else (config.h or via makefile), define the cpp symbol INCLUDE_TEMPLATE_DEFINITION when using GCC. Or something like the following should work as well:

#if __GNUC__ <= 2 && __GNUC_MINOR__ <= 7
# define INCLUDE_TEMPLATE_DEFINITION
#endif



Now gcc will be able to do what you need since it can see the definition when it tries to instantiate XX<int> in main.cc.

Manual instantiation of templates with GCC
Let's start with a trivial program, where a list is the only STL data type used, specifically list<int>. For the examples here, I'm assuming ObjectSpace STL<ToolKit> with GCC 2.6.3 appropriately patched with Jason Merrill template fix (the same idea applies to FSF/GNU STL, but the particular set of templates that need to be instantiated may differ due to implementation differences).

Here's a trivial test program:

#include <stl.h>                // include everything for simplicity.
#include <iostream.h>
#include <stdlib.h>

int main(int, char*[]) {
    list<int> list1;
    for(int i = 0; i < 10; ++i) {
        list1.push_back(rand());
    }
    return 0;
}


Now running the following compile/link commands:
(STL_INCLUDE_DIRS and STL_LIBS depend on your system).

% g++ -fno-implicit-templates -c $(STL_INCLUDE_DIRS) f1.cc
% g++ -o f1 f1.o $(STL_LIBS) |& c++filt
ld: Undefined symbol
   list<int>::list(void)
   list<int>::push_back(int const &)
   list<int>::~list(void)
collect2: ld returned 2 exit status
gmake: *** [f1] Error 1

So all we have to do is to instantiate the members of list<int> listed above. But the problem is that GCC doesn't allow instantiating individual members, so we'll simply instantiate ALL the members for now (until GCC fixes it).

so now I can create a file template-inst.cc:


#include <stl.h>

template class list<int>;



and compile this w/out -fno-implicit-templates option and link it with f1.o and we're in business.

% g++ -c $(STL_INCLUDE_DIRS) template-inst.c
% g++ -o f1 f1.o template-inst.o $(STL_LIBS) |& c++filt
% ./f1
%


Note that using -fno-implicit-templates on the manual instantiation file will get you into lots of trouble. Try it and see. Something to do with some of the static indirect template functions that don't get instantiated indirectly when using -fno-implicit-templates flag.
Now for a bit of reality. Let's say you are going to use the following templated data types in STL:

list<int>
deque<int>

and the following algorithms:

copy (to ostream_iterator)
copy (list to deque)
for_each (on list objects)

#include <stl.h>                // include everything for simplicity.
#include <iostream.h>
#include <stdlib.h>

int main(int, char*[]) {
    deque<int> deque1;
    for(int i = 0; i < 10; ++i) {
        deque1.push_back(rand());
    }

    ostream_iterator<int> out(cout, " ");
    cout << "==> Deque1: ";
    copy(deque1.begin(), deque1.end(), out);
    cout << endl << endl;

    list<int> list1;
    copy(deque1.begin(), deque1.end(),
        back_insert_iterator<list<int> > (list1)
    );

    cout << "==> List1: ";
    copy(list1.begin(), list1.end(), out);
    cout << endl << endl;

    //
    // This nested (within main) struct is going to cause problems later
    // on.
    //
    struct FooBar {
        void operator() (int val) const {
            cout << " " << val;
        }
    };
    cout << "==> For_each List1: ";
    for_each(list1.begin(), list1.end(), FooBar());
    cout << endl << endl;

    return 0;
}


% g++ -fno-implicit-templates -c $(STL_INCLUDE_DIRS) f2.cc
% g++ -o f2 f2.o $(STL_LIBS) |& c++filt
ld: Undefined symbol
   list<int>::list(void)
   deque<int>::deque(void)
   deque<int>::begin(void)
   copy(list_iterator<int>, list_iterator<int>, ostream_iterator<int>)
   copy(deque_iterator<int>, deque_iterator<int>, ostream_iterator<int>)
   copy(deque_iterator<int>, deque_iterator<int>, back_insert_iterator<list<int> >)
   ostream_iterator<int>::ostream_iterator(ostream &, char *)
   deque<int>::end(void)
   for_each(list_iterator<int>, list_iterator<int>, main::FooBar)
   list<int>::~list(void)
   list<int>::begin(void)
   list<int>::end(void)
   back_insert_iterator<list<int> >::back_insert_iterator(list<int> &)
   deque<int>::~deque(void)
   deque<int>::push_back(int const &)
collect2: ld returned 2 exit status
gmake: *** [f2] Error 1

Note the for_each instantiation -- we cannot manually instantiate it because of the main::FooBar, so we simply move FooBar to file scope (not shown here) and redo the compile and link commands.

After moving FooBar to file scope in f2.cc:


% g++ -fno-implicit-templates -c $(STL_INCLUDE_DIRS) f2.cc
% g++ -o f2 f2.o $(STL_LIBS) |& c++filt
ld: Undefined symbol
   list<int>::list(void)
   deque<int>::deque(void)
   deque<int>::begin(void)
   copy(list_iterator<int>, list_iterator<int>, ostream_iterator<int>)
   copy(deque_iterator<int>, deque_iterator<int>, ostream_iterator<int>)
   copy(deque_iterator<int>, deque_iterator<int>, back_insert_iterator<list<int> >)
   ostream_iterator<int>::ostream_iterator(ostream &, char *)
   deque<int>::end(void)


Page : << Previous 9  Next >>