LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices



Reply
 
Search this Thread
Old 06-24-2010, 11:47 AM   #1
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Rep: Reputation: 46
C++: template function instantiation does not work with string as return value. Why?


Hi,

I'm new with templates in C++, but there is something that's really bugging me:

I can make references of a template function from one .cpp file in a .h file and use that function in another .cpp file if I instantiate the template with the appropriate types. But when the return type is std::string, linker errors occur.

EXAMPLE CODE:

main.cpp
Code:
#include "template.h"

int main (int args, char** arg)
{
	test("test");
	test(1);
	test(2);
	test(3.2);
	test(str("test"));
}
template.cpp
Code:
#include <iostream>

#include "template.h"

template <class T>
void test(T t)
{
	std::cout<<t<<std::endl;
}
/*                                             *\
  this template function is instantiated with
  the types int, double and const char*
  so the function can be called from somewhere
  else with these types:
\*                                             */
template void test <int>  (int);
template void test<double>(double);
template void test<const char*> (const char*);

template <class T>
std::string str(T t)
{
	std::string ret=std::string(t);
	return ret;
}
template std::string str <char*> (char*);
EDIT: I changed the char* instantiation to char const*, with the same result (johnsfine spotted this).

template.h
Code:
#include <string>

template <class T>
void test(T);

template <class T>
std::string str(T);
'g++ *.cpp' returns:
Code:
/tmp/ccgjbq5N.o: In function `main':
main.cpp:(.text+0x54): undefined reference to `std::basic_string<char, std::char_traits<char>, std::allocator<char> > str<char const*>(char const*)'
main.cpp:(.text+0x62): undefined reference to `void test<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
collect2: ld returned 1 exit status
The linker only complains about the template function that returns a string, not about the others (if you leave the function with the string entirely out of it, the whole thing links ond works).

Is there a (practicable) solution for this or do I just have to move templates that won't work like that into header files?

Last edited by TITiAN; 06-24-2010 at 06:09 PM. Reason: see "EDIT:"
 
Old 06-24-2010, 04:58 PM   #2
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 453Reputation: 453Reputation: 453Reputation: 453Reputation: 453
Well, I am no C++ expert, but I see 'main.cpp' includes 'template.h', and I do not see that 'template.h' includes 'template.cpp'. Nor 'main.cpp' includes 'template.cpp'.

So, if my observation is correct, why do you have 'template.cpp' in the first place ?
 
Old 06-24-2010, 05:27 PM   #3
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by Sergei Steshenko View Post
Well, I am no C++ expert, but I see 'main.cpp' includes 'template.h', and I do not see that 'template.h' includes 'template.cpp'. Nor 'main.cpp' includes 'template.cpp'.

So, if my observation is correct, why do you have 'template.cpp' in the first place ?
The last code block shows the output of g++ *.cpp. This means that 'template.cpp' is included in compiling the executable. For functions in 'template.cpp' to be called from 'main.cpp', 'template.h' contains references.

This is the usual way to split up code in more than one .cpp file, .h files [=headers] are interfaces between the .cpp files. Here it even works with the template funtions (which are indeed usually just dumped into header files), just the function that returns a string doesn't work that way.

As I stated in my first post, everything works as long as I don't use a template function that returns a string. I'm afraid your reply misses the point (if you have further ideas, you are still invited to post them here).

One thing I can think of is, the string type seems to be a template itself (the other types aren't), which complicates things. But then I can't find out what the problem is. Maybe that one really is too complicated for me, but I'd rather want to know than to guess.

Last edited by TITiAN; 06-24-2010 at 05:32 PM.
 
Old 06-24-2010, 05:38 PM   #4
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,142

Rep: Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127
The failure to find test<std::string>() is easy to understand.

The definition of test is in a separate compilation unit, so you needed to explicitly instantiate every instance of that which you might use. That is what your code is doing with:
Code:
template void test <int>  (int);
template void test<double>(double);
template void test<const char*> (const char*);
So it isn't that std::string is the only type failing to work, but that the above three are the only types that work.

Similarly the failure to find the instantiation of str() needed for str("test"): The compiler has deduced char const* and you simply made the error of providing char* instead.

Edit: The momentary brain lapse below was in my original response. But then I realized I had const&T where you have T, which is why the compiler can't misunderstand "test" for you but did for me:

Similarly I understand the failure to find the instantiation of str() needed for str("test"). But I don't understand why that almost worked and could be fixed easily.

In many places in my own code I have written the equivalent of
template <class T> std::string str(T);
str("test")
and been frustrated that the compiler instantiated something like str<const char[5]> when I really really wanted str<char const*>

So I assumed (without knowing where to check in the C++ standard) that template deduction on "test" produces const char[5] instead of char const*. That has made a lot of my code harder to get right.

But your error messages show a much better behavior by the compiler. It has deduced char const* and you simply made the error of providing char* instead.

Last edited by johnsfine; 06-24-2010 at 05:49 PM.
 
Old 06-24-2010, 05:39 PM   #5
tuxdev
Senior Member
 
Registered: Jul 2005
Distribution: Slackware
Posts: 2,014

Rep: Reputation: 115Reputation: 115
As the compiler is telling you, you don't haven't instantiated what you need, and the compiler can't do it itself because it can't see the implementations of those templates. Each cpp is a processed by itself, nothing from any other cpp is considered ("Translation Unit").

Just save yourself a lot of trouble and put the implementations into the header file, or include from the header file a "template implementation" file.
 
Old 06-24-2010, 05:43 PM   #6
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 453Reputation: 453Reputation: 453Reputation: 453Reputation: 453
Quote:
Originally Posted by TITiAN View Post
...I'm afraid your reply misses the point ...
Probably. I am trying to find answers to the following questions:
  1. Which of the three files: main.cpp, template.cpp, template.h take part in compilation ?
  2. If all three of them, how exactly template.cpp becomes known to the compiler ?
 
Old 06-24-2010, 05:54 PM   #7
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,142

Rep: Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127
Quote:
Originally Posted by Sergei Steshenko View Post
  1. Which of the three files: main.cpp, template.cpp, template.h take part in compilation ?
  2. If all three of them, how exactly template.cpp becomes known to the compiler ?
main.cpp and template.cpp are each compiled, but separately (even though those separate compiles might be invoked by a single g++ command.

So the information in template.h is known to the compiler during each compile (because it is #included in each). But the information in template.cpp is not know while compiling main.cpp and is usable by main.cpp only through linking (which in g++ is able to connect to already instantiated instances of a template from other compilation units, but is not able to instantiate new instances).

Last edited by johnsfine; 06-24-2010 at 05:57 PM.
 
Old 06-24-2010, 06:02 PM   #8
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by Sergei Steshenko View Post
Probably. I am trying to find answers to the following questions:
  1. Which of the three files: main.cpp, template.cpp, template.h take part in compilation ?
  2. If all three of them, how exactly template.cpp becomes known to the compiler ?
main.cpp includes template.h, which is an interface to the functions in template.cpp. The compiler knows about template.cpp because I tell it on the command-line ("g++ *.cpp" < this means all .cpp files in that directory are included in the compilation process). If you are curious about how to implement this (standard) kind of code splitting yourself, this link goes to an elaborate explanation. Splitting up your code is in fact basic C++ knowledge, so I can only recommend that link (or easier explanations on how to do it).

Quote:
Originally Posted by johnsfine View Post
But your error messages show a much better behavior by the compiler. It has deduced char const* and you simply made the error of providing char* instead.
I changed it into const char* too, with the same result I'm afraid. Maybe something interesting will come up here (otherwise, I'll just put those templates into header files)

Last edited by TITiAN; 06-24-2010 at 06:15 PM. Reason: logical error in the last paragraph (I didn't just 'try' const char*, I changed the instantiation into that)
 
Old 06-24-2010, 06:06 PM   #9
johnsfine
Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,142

Rep: Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127Reputation: 1127
Quote:
Originally Posted by TITiAN View Post
I tried it with const char* too, with the same result I'm afraid. Maybe something interesting will come up here (otherwise, I'll just put those templates into header files)
Did you read my explanation of both bugs in your code? Did you try both corrections?

I don't believe you would get the same result. Even fixing just one of the two bugs should have changed the error messages.

Repost your code with your understanding of those two corrections and with the error messages if any from recompiling.

Last edited by johnsfine; 06-24-2010 at 06:07 PM.
 
Old 06-24-2010, 06:24 PM   #10
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by johnsfine View Post
Did you read my explanation of both bugs in your code? Did you try both corrections?

I don't believe you would get the same result. Even fixing just one of the two bugs should have changed the error messages.

Repost your code with your understanding of those two corrections and with the error messages if any from recompiling.
I'm sorry, I thought changing "char*" into "char const*" in the instantiation was the only thing you ment. Changing it did have the same result, even though "char const*" is the correct type.

So what was the other bug? I read your posts, but I don't see anything besides the "char const*" issue.

Last edited by TITiAN; 06-24-2010 at 06:26 PM.
 
Old 06-24-2010, 06:27 PM   #11
Sergei Steshenko
Senior Member
 
Registered: May 2005
Posts: 4,481

Rep: Reputation: 453Reputation: 453Reputation: 453Reputation: 453Reputation: 453
Quote:
Originally Posted by TITiAN View Post
... The compiler knows about template.cpp because I tell it on the command-line ("g++ *.cpp" < this means all .cpp files in that directory are included in the compilation process).
...
AFAIK C++ files are compiled separately, i.e. declarations from one file are not known in another. That's why, I think, files with template code need to be physically included into the files which instantiate the template code.
 
Old 06-24-2010, 06:44 PM   #12
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by Sergei Steshenko View Post
AFAIK C++ files are compiled separately, i.e. declarations from one file are not known in another. That's why, I think, files with template code need to be physically included into the files which instantiate the template code.
The "instantiation" turns templates into full-fledged functions, which can be referred to in headers. They only work as those that you instantiate in the .cpp file, though.

Here is a working version (without string as return parameter), so you get the idea:

main.cpp
Code:
#include "template.h"

int main (int args, char** arg)
{
	test("test");  // const char*
	test(1);       // int
	test(2);       // int
	test(3.2);     // double
}
template.cpp
Code:
#include <iostream>

//    template function:
template <class T>
void test(T t)
{
	std::cout<<t<<std::endl;
}
//   instantiations for int, double and const char*:
template void test <int>  (int);
template void test<double>(double);
template void test<const char*> (const char*);
template.h
Code:
#include <string>

//  reference to the template function:
template <class T>
void test(T);
This is a combination of templates and code splitting, which I also want to implement with template functions that return a string, but I ran into the mentioned trouble...

Compile this with the command "g++ main.cpp template.cpp" or just "g++ *.cpp".

Any ideas appreciated

I gtg soon, will check on later responses when I'm back at my PC again

Last edited by TITiAN; 06-24-2010 at 06:56 PM. Reason: added comments and how to compile, and further comments
 
Old 06-25-2010, 02:17 AM   #13
bigearsbilly
Senior Member
 
Registered: Mar 2004
Location: england
Distribution: FreeBSD, Debian, Mint, Puppy
Posts: 3,314

Rep: Reputation: 175Reputation: 175
your template should be in the header file.

template.cpp should be template.hpp.
template.cpp is not needed.

then it compiles.
 
Old 06-25-2010, 03:46 AM   #14
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by bigearsbilly View Post
your template should be in the header file.

template.cpp should be template.hpp.
template.cpp is not needed.

then it compiles.
Good morning,

I know that much. That would be common practice. I'm just wondering why the instantiation method does work with simple types and does not work with the string type (note that I also posted a fully working example).

Or rather, I would like to know if it's possible to instantiate template functions that return a string and use them from different .cpp files (or object files or translation units, you get the idea). Again, this does work in the second example I posted, but not when the return type is string.
 
Old 06-25-2010, 04:24 AM   #15
TITiAN
Member
 
Registered: Mar 2008
Location: NRW, Germany
Distribution: Debian GNU/Linux with XFCE and packages from "testing"
Posts: 377

Original Poster
Rep: Reputation: 46
Quote:
Originally Posted by johnsfine View Post
Did you read my explanation of both bugs in your code? Did you try both corrections?

I don't believe you would get the same result. Even fixing just one of the two bugs should have changed the error messages.

Repost your code with your understanding of those two corrections and with the error messages if any from recompiling.
I just had another look. The "char*" issue did raise up another linker error, I just didn't look at it closely.

I also spotted another mistake: the "test" function was not instantiated with the string type. After I added that instantiation, everything worked. johnsfine already explained that in his first post, and I only understood that now. Sorry for that.

So everything was just a few mistakes on my part. Thank you for your contributions.

The following code works completely, I'm just posting it for convenience:

main.cpp
Code:
#include "template.h"

int main (int args, char** arg)
{
	test("test");       // char const*
	test(1);            // int
	test(2);            // int
	test(3.2);          // double
	test(str("test"));  // string
}
template.cpp
Code:
#include <iostream>

#include "template.h"

template <class T>
void test(T t)
{
	std::cout<<t<<std::endl;
}
template void test <int>  (int);
template void test<double>(double);
template void test<const char*> (const char*);
template void test<std::string>  (std::string);

template <class T>
std::string str(T t)
{
	std::string ret=std::string(t);
	return ret;
}
template std::string str <char const*> (char const*);
template.h
Code:
#include <string>

template <class T>
void test(T);

template <class T>
std::string str(T);

Last edited by TITiAN; 06-25-2010 at 04:34 AM. Reason: comments
 
  


Reply

Tags
cpp, header, return, split, string, template


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
C++: different instances of a function template unihiekka Programming 3 02-05-2009 02:50 PM
C++ Template For Generic Function - Is This Possible? taylor_venable Programming 6 06-05-2006 09:46 PM
Standard Template Library Reference (std::string) lucky6969b Programming 3 03-15-2006 04:09 PM
can a function return a string? hubabuba Programming 13 03-06-2005 03:51 PM
function prototypes in template class' (c++) qwijibow Programming 4 12-13-2004 10:34 AM


All times are GMT -5. The time now is 08:56 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration