2

I'm patching up security flaws in a 15-20 year old system that hasn't been maintained for 10 years. As such, most of my "patches" are really work arounds and kludges because a real solution would erode the integrity of a working (if insecure) system. One of the "patches" I applied was related to my old compiler not being able to find to_string() within std, which works pretty well, but not perfectly.

Somewhere in these thousands of lines of code, I try to convert a double using patch::to_string(), and it fails to compile with the following error:

    g++  -o main -Wall -g -Wextra -pedantic main.o veracodefunctions.o
main.o: In function `main':
/home/(omited for privacy)/2_Test/main.cpp:8: undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> > patch::to_string<double>(double const&)'
collect2: ld returned 1 exit status
*** Error code 1
clearmake: Error: Build script failed for "main"

Below is the original implementation of the patch I used, which I got from the above Stack Overflow post, and should work:

#include <string>
#include <sstream>

namespace patch
{
    template < typename T > std::string to_string( const T& n )
    {
        std::ostringstream stm ;
        stm << n ;
        return stm.str() ;
    }
}

#include <iostream>

int main()
{
    std::cout << patch::to_string(1234) << '\n' << patch::to_string(1234.56) << '\n' ;
}

The patch by cMinor has this main() at the bottom of it that is supposed to illustrate that it works with both int and double values, but for some reason mine only works with int, and not with double. Here is my implementation:

patch.h:

8 #include <string>
...
25 namespace patch
26 {
27    template < typename T > std::string to_string( const T& n );
28 }

patch.cpp:

6 #include <string>
7 #include <sstream>
8 #include <stdexcept>
9 #include "patch.h"
...
41 namespace patch
42 {
43    template < typename T > std::string to_string( const T& n )
44    {
45       std::ostringstream stm;
46       stm << n;
47       return stm.str();
48    }
49 }

main.cpp:

2 #include <iostream>
3 #include "patch.h"
4 
5 int main () {
6 
7    std::cout << "\"12345\" to string is: " << patch::to_string(12345) << std::endl;
8    std::cout << "\"12.345\" to string is: " << patch::to_string(12.345) << std::endl;
9    return 0;
10 
11 }

And just so you don't have to scroll, here is the compile error again:

    g++  -o main -Wall -g -Wextra -pedantic main.o veracodefunctions.o
main.o: In function `main':
/home/(omited for privacy)/2_Test/main.cpp:8: undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> > patch::to_string<double>(double const&)'
collect2: ld returned 1 exit status
*** Error code 1
clearmake: Error: Build script failed for "main"

Any insight would be much appreciated!

Community
  • 1
  • 1

2 Answers2

1

You only declared to_string() in the header, and defined it in the cpp files. While this is the normal way with non-template functions, this is incorrect with templates. You need to place the definition (the actual implementation) in the .h header file, not in the .cpp file.

patch.h:

#include <string>
#include <sstream>
#include <stdexcept>

namespace patch
{

    template < typename T > std::string to_string( const T& n )
    {
       std::ostringstream stm;
       stm << n;
       return stm.str();
    }
}
galinette
  • 8,896
  • 2
  • 36
  • 87
  • I thought I did. Could you be a little more specific? I don't fully understand what you mean by that. –  Apr 28 '15 at 20:41
  • Ah, so the whole definition should be in the .h, and there should be nothing in the .cpp file? –  Apr 28 '15 at 20:43
  • I'll try this now, and get back to you. Thanks! –  Apr 28 '15 at 20:44
  • Yes. The compiler needs to have the full body in every translation unit when you use templates. – galinette Apr 28 '15 at 20:45
  • It compiles now! Thank you. I find it odd though that it would compile and run if I was only using `to_string()` on an `int`, but not on a `double`. Do you have an explanation for that? –  Apr 28 '15 at 20:48
  • That's actually strange and not standard. The compiler may somehow manage to reduce the template to a non-template if only one type is used? – galinette Apr 28 '15 at 20:52
  • Kinda bizarre, huh? Anyway, thank you again. Glad to learn something today. Enjoy the day! –  Apr 28 '15 at 20:55
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/76501/discussion-between-galinette-and-makenbaccon). – galinette Apr 29 '15 at 05:54
0

When you instantiate a header type, the compiler will create a new class for that instantiation. IF the implementation of your function is not in a header, it will not be accessible at compile time which is why you are getting a linker error.

This question has been mostly answered here: Why can templates only be implemented in the header file?

Community
  • 1
  • 1
Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175