Strange linking error with template

S

sleepydj

Hello,

I have a simple program to create strings from the corresponding
double/integer values. For any data type similar to int, I have a
template set up in a file misc.h:

*************************************************************************************
//misc.h
#ifndef __MISC_H__
#define __MISC_H__

#include <string>

//!Convert any numerical quantity (int, long, uint*_t, time_t, etc) to
a string
template <class X> string numtostr(X number);


//I have explicitly overloaded it for double:
//!Convert a double to a string
string numtostr(double number);

#endif


I have their corresponding bodies in the file misc.cpp. In test.cpp ,
I have the main() function (quite simple - see below).

***********************************************************************************
//test.cpp
#include <iostream>
#include <string>

#include "misc.h"

using namespace std;


int main()
{
int a=30;

cout<<numtostr(a)<<endl;

return 0;
}

************************************************************************************
//misc.cpp
#include <iostream>
#include <cstdlib>
#include <string>
#include <vector>

#include "misc.h"

using namespace std;

template <class X> string numtostr(X number)
{
//get the length of the string
char junk;
size_t len = snprintf(&junk,1,"%d",number);

//temporary buffer to hold the string
vector<char> temp(len);
sprintf(&(temp.at(0)), "%d",number);


//put the data in the string
string result(temp.begin(), temp.end());

return result;
}

string numtostr(double number)
{
//get the length of the string
char junk;
size_t len = snprintf(&junk,1,"%f",number);

//temporary buffer to hold the string
vector<char> temp(len);
sprintf(&(temp.at(0)), "%f",number);

//put the data in the string
string result(temp.begin(), temp.end());

return result;
}
**************************************************************************************


When I try to compile this program using "g++ -o test test.cpp
misc.cpp", I get the following error:

/tmp/cc6tDQzg.o: In function `main':
test.cpp:(.text+0x18): undefined reference to `std::basic_string<char,
std::char_traits<char>, std::allocator<char> > numtostr<int>(int)'
collect2: ld returned 1 exit status

I'm not sure why it's unable to find that template. If in main(), I
change it to float a=35.5; then it works perfectly!

I also tried to put everything in just one file. And then when I tried
to compile it, it works fine for both int and double!

I'm completely at a loss as to what is happening. Your help will be
appreicated! Just for some more details, g++ --version gives:

g++ (GCC) 4.0.1 (Debian 4.0.1-2)
Copyright (C) 2005 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is
NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.


Thanks,
Jimmy
 
G

Greg Comeau

//misc.h ....
template <class X> string numtostr(X number);
...
I have their corresponding bodies in the file misc.cpp. In test.cpp ,
I have the main() function (quite simple - see below).
....
When I try to compile ... I get the following error:

/tmp/cc6tDQzg.o: In function `main':
test.cpp:(.text+0x18): undefined reference to `std::basic_string<char,
std::char_traits<char>, std::allocator<char> > numtostr<int>(int)'
collect2: ld returned 1 exit status
...

See http://www.comeaucomputing.com/techtalk/templates/#whylinkerror
 
S

sleepydj

Hi,

Oh I managed to figure it out (thanks to a friend!). Apparently, the
template function body must always be defined in the header file. The
reason being that template functions are actually expanded for each
data type by the compiler. The cpp file is only compiled once whereas
the header file is compiled as many times as it is included. So when
the template function is defined in the header file, it allows the
compiler to customize it to every data type as many times as necessary!

I hope that is the right explanation (and that it makes sense!)

Thanks,
Jimmy
 
I

Ian

sleepydj said:
Hi,

Oh I managed to figure it out (thanks to a friend!). Apparently, the
template function body must always be defined in the header file. The
reason being that template functions are actually expanded for each
data type by the compiler. The cpp file is only compiled once whereas
the header file is compiled as many times as it is included. So when
the template function is defined in the header file, it allows the
compiler to customize it to every data type as many times as necessary!

I hope that is the right explanation (and that it makes sense!)
It is for compilers that require the bodies to be visible in the header.
Some do, some don't.

Ian
 
P

Peter_Julian

| Hello,
|
| I have a simple program to create strings from the corresponding
| double/integer values. For any data type similar to int, I have a
| template set up in a file misc.h:
|
|
************************************************************************
*************
| //misc.h
| #ifndef __MISC_H__
| #define __MISC_H__
|
| #include <string>
|
| //!Convert any numerical quantity (int, long, uint*_t, time_t, etc) to
| a string
| template <class X> string numtostr(X number);

there is no such type called "string", try a std::string.

something like:

template< class N >
std::string numtostr(N n);

|
|
| //I have explicitly overloaded it for double:
| //!Convert a double to a string
| string numtostr(double number);

the proper term is a template "specialization":

template<>
std::string numtostr< double >(double d);

<snip>

| //misc.cpp
| #include <iostream>
| #include <cstdlib>
| #include <string>
| #include <vector>
|
| #include "misc.h"
|
| using namespace std;
|
| template <class X> string numtostr(X number)
| {
| //get the length of the string
| char junk;
| size_t len = snprintf(&junk,1,"%d",number);
|
| //temporary buffer to hold the string
| vector<char> temp(len);
| sprintf(&(temp.at(0)), "%d",number);
|
|
| //put the data in the string
| string result(temp.begin(), temp.end());
|
| return result;
| }

<snip>

|
| I also tried to put everything in just one file. And then when I tried
| to compile it, it works fine for both int and double!
|

in the case you prefer not seperating the interface and
implementation...

#include <string>
#include <sstream>

template< class N >
std::string TtoString(N n)
{
ostringstream oss;
oss << n;
return oss.str();
}

stringstreams are worth learning...

<snip>

In the case you decide to seperate header and source, try using template
specialization...

---
// Misc.h

#ifndef MISC_H_
#define MISC_H_

#include <string>

template< class N >
std::string numtostr(N n);

template<>
std::string numtostr< int >(int n);

template<>
std::string numtostr< double >(double d);

template<>
std::string numtostr< long >(long d);

#endif // MISC_H_
---
// Misc.cpp
#include "Misc.h"
#include <iostream>
#include <sstream>
using std::string;
using std::eek:stringstream;

template<>
string numtostr< int >(int n)
{
ostringstream oss;
oss << n;
return oss.str();
}

template<>
string numtostr< double >(double d)
{
ostringstream oss;
oss << d;
return oss.str();
}

template<>
string numtostr< long >(long l)
{
ostringstream oss;
oss << l;
return oss.str();
}
---
// test.cpp
#include "Misc.h"
#include <iostream>
#include <string>

int main()
{
std::string s;

int n(-11);
s += numtostr(n);
s += ", ";

double d(11.1111);
s += numtostr(d);
s += ", ";

long l(1111);
s += numtostr(l);

std::cout << "the sequence of numbers is: ";
std::cout << s << std::endl;

return 0;
}

/* output:

the sequence of numbers is: -11, 11.1111, 1111

*/
 

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,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top