linker errors with templates

F

Fabio De Francesco

Hello.

I can't understand why I can't compile the following simple code, where
I think I have applied all the needed rules for templates that are
declared and defined in different files (*.h and *.cpp).

What amazes me is that I have already some code like this in another
project where I don't get errors, so I am pretty sure I am missing some
stupid thing.

// file templates.h

#ifndef _TMPLT_
#define _TMPLT_

#include <iostream>

template <typename T>
class my_class;
template <typename T>
std::eek:stream& operator<<( std::eek:stream& out, const my_class<T>& obj );

template <typename T>
class my_class
{
public:
my_class( const T& v );
private:
friend std::eek:stream& operator<< <> (std::eek:stream& out,const
my_class<T>& obj);
const T val;
};

#endif

// file templates.cpp

#include "templates.h"

template class my_class<int>;

template <typename T>
my_class<T>::my_class( const T& v = T() )
: val( v )
{ }

template <typename T>
inline std::eek:stream& operator<<( std::eek:stream &out, const my_class<T>
&obj )
{
return out << obj.val << '\n';
}

// file main.cpp

#include "templates.h"

int main()
{
my_class<int> h1(99);
std::cout << h1;
return 0;
}

I compile it with gcc-3.4.1:

myself@myhost /temporary # g++ -c templates.cpp -o templates.o -Wall
myself@myhost /temporary # g++ main.cpp templates.o -Wall
/tmp/ccx0vMtF.o(.text+0x144): In function `main':
: undefined reference to `std::basic_ostream<char,
std::char_traits<char> >& operator<< <int>(std::basic_ostream<char,
std::char_traits<char> >&, my_class<int> const&)'
collect2: ld returned 1 exit status

Thanks in advance to everyone that points me to the errors in code.
Ciao,

Fabio De Francesco
 
F

Fabio De Francesco

Maybe my post wasn't clear, due to my poor English...

I have already read the FAQ, this is the problem. What I said is that I
have some other similar code that is working by following the same
rules as the FAQ says.

Can you please have a closer look at the code?
Thank you,

Fabio De Francesco
 
J

Jonathan Mcdougall

Fabio said:
Maybe my post wasn't clear, due to my poor English...

I have already read the FAQ, this is the problem. What I said is that I
have some other similar code that is working by following the same
rules as the FAQ says.

In most compilers today, you need to put the definition of a class
template's member functions in the class' definition, *not* in an
implementation file. Please, look closely at
http://www.parashift.com/c++-faq-lite/containers-and-templates.html#faq-34.13
and the following. And while you're at it, browse the whole FAQ.

If your code has already worked, it means the compiler you used was
probably able to implement the export keyword. Currently, afaik, only
Comeau does.


Jonathan
 
V

Victor Bazarov

Fabio said:
Maybe my post wasn't clear, due to my poor English...

I have already read the FAQ, this is the problem. What I said is that I
have some other similar code that is working by following the same
rules as the FAQ says.

You have _similar_ code working, but not this one. What specifically
makes the codes different? If you can't figure it out, how can we without
seeing the other code?
Can you please have a closer look at the code?

I have. You declare a function template 'operator<<'. You never give
the compiler a command to instantiate it (using explicit instantiation)
or give it the definition of the function when it is ready to produce
an implicit instantiation (while compiling 'main').

Either add an explicit instantiation or put the body in the header.

V
 
F

Fabio De Francesco

Thank you for your reply. But I am not able to understand yet.

What is meant by "explicit instantiation"?

Can you please show me where and how you would put it in my code?

The other code that I can compile with the same compiler and without
errors is the following one:

// file database.h

#ifndef _DATABASE_H_
#define _DATABASE_H_

#include <iostream>
#include <fstream>
#include <string>

using std::eek:stream;
using std::fstream;
using std::string;

template <typename T>
class DataBase;

template <typename T>
ostream& operator<<( ostream &out, DataBase<T> &db ); // friend
template functions must be forward declared

template <typename T>
class DataBase
{
public:
DataBase();
private:
fstream dataFile;
string fName;
void run();
void add( T & );
// functions skipped, not required for compiling
ostream& print( ostream & );
friend ostream& operator<< <> ( ostream &out, DataBase<T> &db );
// Note the unusual symbol <>
};

#endif


// file database.cpp

#include "person.h"
#include "database.h"

using std::ios;

template class DataBase<Person>;

template <typename T>
DataBase<T>::DataBase()
{
cin.clear();
while ( true )
{
cout << "\nEnter DataBase complete path: ";
if ( ( cin >> fName ) || ! cin.eof() )
{
if ( cin.good() )
this->run();
else
{
cin.clear();
cin.ignore(
std::numeric_limits<std::streamsize>::max(), '\n' );
cout << "\nBad input!";
}
}
else
{
cin.clear();
break;
}
}
}

template <typename T>
void DataBase<T>::run()
{

int option;
T record;
do
{
cout << "1. Add 2. Find 3. Modify 4. Exit\n";
if ( !( cin >> option ) || ( option < 1 ) || ( option > 4 ) )
{
cout << "Bad option!\n\n";
cin.clear();
cin.ignore( std::numeric_limits<std::streamsize>::max(),
'\n' );
continue;
}
switch ( option )
{
case 1:
cin >> record; // overloaded ">>"
add( record );
break;
// lot of code has been skipped, but it is possible to
compile without it
}
}
while ( option != 4 );
cout << "End of DataBase use\n\n";
cout.flush();
}

template <typename T>
void DataBase<T>::add( T &record )
{
dataFile.open( fName.c_str(), ios::eek:ut | ios::app );
dataFile.seekp(0,ios::end);
record.writeToFile(dataFile);
dataFile.close();
}

template <typename T>
inline ostream& operator<<( ostream &out, DataBase<T> &db )
{
return db.print( out );
}

// file usedb.cpp

#include "person.h"
#include "database.h"

int main()
{
DataBase<Person> dbPerson;
return 0;
}

The above copied code works and I am not able to see any difference
from the one that I posted at the thread start. I had to cut a lot of
code but what has been left can be compiled.

While wainting for your next reply, I thank you again.
Fabio De Francesco
 
V

Victor Bazarov

Fabio said:
Thank you for your reply. But I am not able to understand yet.

What is meant by "explicit instantiation"?

Doesn't the book you read to understand templates tell you about it?
Can you please show me where and how you would put it in my code?

You already have one. See below.
The other code that I can compile with the same compiler and without
errors is the following one:

// file database.h

#ifndef _DATABASE_H_
#define _DATABASE_H_

#include <iostream>
#include <fstream>
#include <string>

using std::eek:stream;
using std::fstream;
using std::string;

template <typename T>
class DataBase;

template <typename T>
ostream& operator<<( ostream &out, DataBase<T> &db ); // friend
template functions must be forward declared

template <typename T>
class DataBase
{
public:
DataBase();
private:
fstream dataFile;
string fName;
void run();
void add( T & );
// functions skipped, not required for compiling
ostream& print( ostream & );
friend ostream& operator<< <> ( ostream &out, DataBase<T> &db );
// Note the unusual symbol <>
};

#endif


// file database.cpp

#include "person.h"
#include "database.h"

using std::ios;

template class DataBase<Person>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is an explicit instantiation.
template <typename T>
DataBase<T>::DataBase()
{
cin.clear();
while ( true )
{
cout << "\nEnter DataBase complete path: ";
if ( ( cin >> fName ) || ! cin.eof() )
{
if ( cin.good() )
this->run();
else
{
cin.clear();
cin.ignore(
std::numeric_limits<std::streamsize>::max(), '\n' );
cout << "\nBad input!";
}
}
else
{
cin.clear();
break;
}
}
}

template <typename T>
void DataBase<T>::run()
{

int option;
T record;
do
{
cout << "1. Add 2. Find 3. Modify 4. Exit\n";
if ( !( cin >> option ) || ( option < 1 ) || ( option > 4 ) )
{
cout << "Bad option!\n\n";
cin.clear();
cin.ignore( std::numeric_limits<std::streamsize>::max(),
'\n' );
continue;
}
switch ( option )
{
case 1:
cin >> record; // overloaded ">>"
add( record );
break;
// lot of code has been skipped, but it is possible to
compile without it
}
}
while ( option != 4 );
cout << "End of DataBase use\n\n";
cout.flush();
}

template <typename T>
void DataBase<T>::add( T &record )
{
dataFile.open( fName.c_str(), ios::eek:ut | ios::app );
dataFile.seekp(0,ios::end);
record.writeToFile(dataFile);
dataFile.close();
}

template <typename T>
inline ostream& operator<<( ostream &out, DataBase<T> &db )
{
return db.print( out );
}

// file usedb.cpp

#include "person.h"
#include "database.h"

int main()
{
DataBase<Person> dbPerson;

Yes, but in this file you don't make any attempt to use the operator<<.
And if you did, it wouldn't compile either.
return 0;
}

The above copied code works and I am not able to see any difference
from the one that I posted at the thread start.

Too bad.
I had to cut a lot of
code but what has been left can be compiled.

That's commendable. Posting minimal code that still does what you say is
what is usually needed for this newsgroup.

V
 
F

Fabio De Francesco

Right in that FAQ is explained how to avoid those linker errors. And I
think I am following their suggestions that don't require to put the
definition of a class template's member function in the class
definition inside the .h file.
Please read the document from "The solution is to convince the compiler
while it is examining the class body proper ..."
Ciao,

Fabio De Francesco
 
V

Victor Bazarov

Fabio said:
Right in that FAQ is explained how to avoid those linker errors. And I
think I am following their suggestions that don't require to put the
definition of a class template's member function in the class
definition inside the .h file.

That only resolves the linker errors regarding the class members. If you
have other templates, like functions, like your operator<<, for example,
wouldn't it be logical to extend the suggestion and put the definition of
those/that function into the header too?
Please read the document from "The solution is to convince the compiler
while it is examining the class body proper ..."

But your compiler is complaining about a non-member function, isn't it?

V
 
F

Fabio De Francesco

Ah, now I understand the issue.

I have been confused about the meaning of "explicit instantiation"
because I supposed that the line "template class my_class<int>;" was
the only "explicit instantiation" the program needed.

After your last reply I tried to include a similar explicit
instantiation also for the friend function like the following:

template std::eek:stream& operator<<( std::eek:stream& out, const
my_class<int>& obj );

and that made the program to compile and to work.

I didn't imagine to be required to add that line because it is not
required for other functions in the class. Maybe I am too lazy... but I
don't see where this is showed in the FAQ (at section 34.15). What I
see there is only the class explicit instantiation, but here is about
midnight.

If you have a look at section 34.14 you can see another omission, that
is when the author writes the explicit instantiation of a class it
writes only "template Foo<int>;" instead of "template class Foo<int>;".
Do you think is it correct? My compiler doesn't work without the
"class" in it.

Thank you again,

Fabio De Francesco
 
V

Victor Bazarov

Fabio said:
Ah, now I understand the issue.

I have been confused about the meaning of "explicit instantiation"
because I supposed that the line "template class my_class<int>;" was
the only "explicit instantiation" the program needed.

After your last reply I tried to include a similar explicit
instantiation also for the friend function like the following:

template std::eek:stream& operator<<( std::eek:stream& out, const
my_class<int>& obj );

and that made the program to compile and to work.
Good.

I didn't imagine to be required to add that line because it is not
required for other functions in the class. Maybe I am too lazy...

The 'operator <<' is not a "function in the class", is it?
but I
don't see where this is showed in the FAQ (at section 34.15). What I
see there is only the class explicit instantiation, but here is about
midnight.

If you have a look at section 34.14 you can see another omission, that
is when the author writes the explicit instantiation of a class it
writes only "template Foo<int>;" instead of "template class Foo<int>;".
Do you think is it correct? My compiler doesn't work without the
"class" in it.

You should bring both issues to the attention of Marshall Cline, the
maintainer of the FAQ. The link to his e-mail is on the front page of
the FAQ.

V
 
F

Fabio De Francesco

Victor said:
The 'operator <<' is not a "function in the class", is it?


You should bring both issues to the attention of Marshall Cline, the
maintainer of the FAQ. The link to his e-mail is on the front page of
the FAQ.

V

If you agree with my observations about those FAQ's sections, please
would you bring those issues to the attention of Mr. Cline? I don't
write a good enough English at a level I think it is required for
commenting on the document and proposing changes. Furthermore I don't
have enough knowledge of the C++ language required by that task.

So, please would you (or someone else with the same knoledge of the
issues)do it?

Thank you,

Fabio De Francesco
 
V

Victor Bazarov

Fabio said:
[...]
If you agree with my observations about those FAQ's sections,

Actually, I don't. Just look at 34.13. But if you still feel that
you have some suggestions how to improve the FAQ, please address
Marshall yourself.
please
would you bring those issues to the attention of Mr. Cline? I don't
write a good enough English at a level I think it is required for
commenting on the document and proposing changes. Furthermore I don't
have enough knowledge of the C++ language required by that task.

If you feel you know enough English to argue with me about who should
e-mail Marshall, then you know enough English to e-mail him yourself.

As to the knowledge of C++, the only thing that matters is _his_ C++
level, which is sufficient. If there is an error or omission in the FAQ,
it will be corrected, and if there isn't, you will be told.
So, please would you (or someone else with the same knoledge of the
issues)do it?

If you won't do it, it won't be done.

V
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top