Templates and .cpp files - c++

I am learning C++ and it hasn't been an enjoyable experience (compared to Java or VBA at least). I have the following code:
//This is in a number.h file
#pragma once
template <class T>
class number{
public:
T value1, value2, result;
public:
T add();
number(T value1_in, T value2_in);
};
//This is in a number.cpp file
template <class T>
number<T>::number(T value1_in, T value2_in){
value1 = value1_in;
value2 = value2_in;
}
template <class T>
T number<T>::add(){
result = value1 + value2;
return result;
}
//This is in main.cpp
#include "number.h"
#include <iostream>
using namespace std;
int main(){
int a = 2, b =3;
number<int> n1(a,b);
cout << n1.add();
system("pause");
return EXIT_SUCCESS;
}
Which of course gives me an error. Even though I am pretty sure it should work. More specifically I get a linker error. After 3 hours of looking at this I decided to include number.cpp in main.cpp and that magically made it work. What the hell is going on? I thought I only need to include the header file (I wrote a matrix class with a bunch of linear solvers for different algorithms before this and only included header files in the whole project). Is this C++ specific or compiler specific? (I am using Dev-C++ 4.9.9.2 which has Mingw I guess)

You do not have to put the entire definition into the header file; don't listen to what others tell you :-)
That is the conventional solution, and you and I will do it quite often, but it's not the only solution.
The other option is to simply place this line at the end of your number.cpp file, in order to force that particular template class to be instantiated and fully compiled there.
template class number<int>;
In short, there are two valid solutions. You can use this line, or you can copy the definition into the header file. Depending on context, one approach might be better than the other, but both are valid. See this answer of mine for a more comprehensive discussion of both approaches:
https://stackoverflow.com/a/8752879/146041

Templated classes or functions always need to stay in the header file.
The reason is that whenever you instantiate a template, the preprocessor (inside compiler) generates new code for exactly that kind of instantiation (e.g. number<double>). That's why the classes number<double> and number<int> will not share any relationship: They will be two completely different classes although both were generated from the same template.
For the compiler to be able to generate this code, it must know the whole template definition, not only its declaration. That's why a template needs to stay in the header in full.
Including the cpp file in your main.cpp did the trick, as it effectively became a header.

In C++, templates are just as their name suggests: templates for a class, function etc. As the concrete type of the template parameter is not known in advance, and the compiled object code depends on the actual parameter, they are not getting compiled (as normal classes and functions) until you use them in any other source file and the compiler gets to know what type should it substitute into the template parameter.
That's why all functions must be defined and implemented in the header file.
See the last section of this documentation or the answers for this similar question for further explanation.

Related

How come you can split non template classes in a .h interface and .cpp implementation?

I understand that if you attempt to split a templated class in a .h interface and a .cpp implementation, you get a linker error. The reason for this as mentioned in a popular post is "If the implementations were not in the header, they wouldn't be accessible, and therefore the compiler wouldn't be able to instantiate the template."
What I dont understand is that if the implementations inside a .cpp file are inaccessible in case of templated classes, what makes them accessible for non templated or just regular classes. How come we are able to split the interface and implementation for normal classes over a .h and .cpp file without getting a linker error?
Test.h
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val);
Test.cpp
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
// Process it here.
return val;
}
Main.cpp
void main()
{
int a, b;
b = ProcessVal(a);
}
This code gives linker error. A similar splitting of non templated classes does not give Linker error. I could post the code, but you get the idea.
In case of plain function, compiler generates code straightaway and adds generated code to compilation unit.
Test.cpp
int ProcessVal(int val)
{
// Process it here.
return val;
}
In case of the above code all the necessary information is known and C++ code of the function ProcessVal can be translated into machine instructions. As a result, object file (probably called Test.o) will contain ProcessVal symbol + corresponding code and linker can refer to it (to generate calls or to perform inlining).
On the other hand this piece of code:
Test.cpp
template<typename TypeOne>
TypeOne ProcessVal(TypeOne val)
{
// Process it here.
return val;
}
does not provide any output to compilation unit. Object file of this compilation unit (Test.o) will not contain any code for ProcessVal() function, because compiler does not know of what type TypeOne argument is going to be. You have to instantiate template to get its binary form and only this can be added to resulting binary.
When you have a template definition, nothing is added to the compilation unit, because the template argument could be many things, so you can't know from compile time what class to create, as stated in this answer.
With the non-templated case, you know what you have inside your class, you don't have to wait for a template argument to be given in order to really generate the actual class, thus the linker can see them (since they are compiled into the binary as doc's post puts it).
Basically, this goes back to the old C language. The .h files were intended to be done with the C preprocessor, and were literally textually included into the C source by the preprocessor. So if you had foo.h:
int i = 0;
and foo.c:
#include "foo.h"
int main(){ printf("%d\n", i);}
when the preprocessor was done, the compiler actually saw:
int i = 0;
int main(){ printf("%d\n", i);}
as the source file. There was no chance for a linker error, because the linker was never involved -- you just compiled one C file.
While the semantics of templates are a little more complicated now, the programming model is still the same: your .h file includes program text that is introduced to the program lexically, before actual final parsing and compilation takes place.
If you actually do want to have the implementation of a template function or a class in a separate C++ file, you can explicitly instantiate it for a certain type. For instance, in your particular example if you add this line to test.cpp, your code will successfully link
template int ProcessVal<int>(int val);

Can I put `extern template` into a header file?

Is putting an extern template in a header file and then do the explicit template instantiation in a unit compilation file valid ?
For example in the compiling example for g++, is this working to avoid the instancation of nothing<int> twice ? Why doesn't anybody write it like this and prefer to copy pase the extern template line in each .cpp file ?
A.hpp:
#ifndef HEADERC_A
#define HEADERC_A
template< typename T > struct nothing {};
extern template struct nothing<int>;
#endif
A.cpp:
#include "A.hpp"
template struct nothing<int>;
main.cpp:
#include "A.hpp"
#include <iostream>
int main()
{
nothing<int> n;
return 0;
}
Well, this is certainly "valid" insofar as gcc will compile this, and do pretty much what you expect to happen.
As far as why doesn't everyone to do this, well, once you go beyond a trivial situation like this, and you start managing a large collection of widely used templates, it will quickly reach the point where it simply becomes not practical to keep track of every parameter that each one of your templates gets used with, so that it can be instantiated explicitly, in this manner.
It's going to be much easier for the compiler to keep track of it, for you.
As previously stated this is a perfectly valid use case and if it fits your programming model then you should use it. But buyer beware:
There are several reasons why extern templates are not commonly declared in header files and then explicitly instantiated in the cpp files.
A very common model for implementing template classes/functions is to place the definitions in the header file and the implementation in an "inl" or other named file. But then include that file at the bottom of the header file. There are reams of code that use this approach to resolving the template/header/implementation separation problem. Putting an "extern" at the top of the implementation makes the code much easier to read and maintain, especially when multiple classes get involved. Heres an example:
A.hpp
#pragma once
template< typename T > struct nothing {
void donothing(T input); // fastest func around
};
#include "A.inl"
A.inl
// does NOT include A.hpp
extern template struct nothing<int>; // save time and space
template<typename T> nothing<T>::donothing { return; }
Instance.h
#include "A.hpp"
template struct nothing<int>; // compiler generates code now
But there is a hidden caveat in all this...
If this gets implemented as you suggest then what happens when another person comes along and wants:
nothing<float> mynothing;
The compiler will see the header file but never find an implementation for float. So it may compile just fine, but at link time there will be unresolvable symbols.
So they try this:
template struct nothing<float>;
nothing<float> mynothing;
WRONG! Now the compiler can't find the implementation and all you get is MORE errors.
Now you could go back to your A.cpp file and add another instance for float...can you say maintenance, headache, carpal tunnel nightmare? With the commonly used solution you get to have your cake and eat it to. (mostly)
Now you may be thinking why even bother with the extern? Because as your post implies there is a typical use case where most of the time "nothing" will be used with an int template type. Having this occur in potentially hundreds of files can lead to serious compile time and code size ramifications.
Why doesn't the standards committee do something about this mess? They did! They added extern templates! In all fairness it is a difficult issue to resolve after the fact.

Linker errors - undefined references

Yes, I know this has been asked a billion times before, I've checked at least 100 duplicates of this question, and still haven't found an answer.
I'm getting undefined reference errors to all of my LList functions, although it all seems to be properly defined and linked. Since my code is a bit too long to paste here, I made a pastie: Click
I compile my code with: g++ driver.cpp box.cpp LList.cpp Song.cpp Album.cpp -o driver
A class or function template is not a class or function and hence cannot be placed in a .cpp file like classes or functions. Rather, a template is a blueprint for how to make a class or function, namely a particular instantination of the template.
You can solve your problem in two ways:
1 either put all the templated code in the respective header files.
2 or instantinate the code explicitly in the .cpp files. For example
// Llist.cpp
#include Llist.hpp
#include Sonc.hpp
/* definition of members of Llist<> */
template class Llist<Song>; // creates class Llist<Song>
Solution 1 always works, but has the potential for HUGE header files and exposes all (or most) implementation details to the user. Solution 2 avoids huge headers and hides implementation details, but requires that you know which instantination you actually need (often impossible, in particular for such general concepts as linked lists).
Finally, solution 3: use the C++ standard library (std::list, std::forward_list) and don't worry.
Template classes and functions must be defined inline. Thats the problem. For example:
//box.h
#ifndef BOX_H
#define BOX_H
template <typename DataType>
struct box
{
DataType data;
box<DataType> *next;
box(DataType d, box<DataType>* n)
{
data = d;
next = n;
}
};
#endif
And remove the .cpp file; you should do the same to the LList.h/cpp

Undefined reference to ClassName::ClassName

I'm using Code::Blocks to build my project, which contains three files: main.cpp, TimeSeries.cpp, TimeSeries.h. TimeSeries.h provides declarations for the TimeSeries class as follows:
template<class XType, class YType> class TimeSeries {
public:
TimeSeries(void);
~TimeSeries(void);
};
Then TimeSeries.cpp contains:
#include "TimeSeries.h"
template<class XType, class YType>
TimeSeries<XType, YType>::TimeSeries(void) {
}
template<class XType, class YType>
TimeSeries<XType, YType>::~TimeSeries(void) {
}
And finally, main.cpp contains
#include "TimeSeries.h"
typedef TimeSeries<float, float> FTimeSeries;
int main(int argc, char** argv) {
FTimeSeries input_data;
return 0;
}
When building with C::B, I get the following error:
undefined reference to `TimeSeries<float, float>::TimeSeries()'
What can I do?
Thanks,
CFP.
Basically all templated code should be defined in a header, otherwise it will not be built since nothing uses it in the compiled unit.
Each cpp file is compiled as a separate unit, and thus constructor and destructor are not compiled. The compiler has no way of knowing what type of template argument you will use in main.cpp when it compiles TimeSeries.cpp.
The reason for splitting code into header- and source-files is so that the declaration and the implementation are separated. The compiler can translate the source-file (compilation unit) into an object file, and other compilation-units that want to use the classes and functions just include the header-file, and link the object file. This way, the code has to be compiled only once, and can be reused by linking it.
The problem with templates is that, as long as there are no parameters provided for them, the compiler cannot compile them. The same template instantiated with different parameters results in different types. std::vector<int> and std::vector<float> are, from the compilers perspective, not related in any way. Because of this, template-classes usually have to reside completely in a header-file, because, when the template is used, the compiler needs the complete definition in order to generate the class depending on the parameters.
As #Gabriel Schreiber pointed out in his answer, you can tell the compiler that he should compile the template with a specific set of parameters, making that available to other compilation units just by linking. However, this does not make the template available for other parameter sets.
You need to add this in your .cpp-file (below the definitions):
template class TimeSeries<float, float>;
When the compiler compiles TimeSeries.cpp it doesn't know which for which types the template is need because it is used in another source file. You need to tell the compiler explicitly.
Read about Explicit Template Instantiation in your copy of the Stroustrup or on the internet.

c++ class with template cannot find its constructor

I have a problem I don't really understand. I have a class Node.
template<class T>
class node {
protected:
T _data;
public:
node(T data);
};
This is in "node.h" file. In "node.cpp" file, there is this constructor:
#include "node.h"
template<class T>
node<T>::node (T data) {
_data = data;
}
While the compiler finds no error, the linker (ld) tells me:
/usr/bin/ld: Undefined symbols:
node<int>::node(int)
the weird part... if I move the constructor from .cpp to .h file, everything works fine. Where is the problem?
The problem is that templates aren't classes - you don't normally write them in two separate files. Template classes are code that the compiler uses to generate classes. As such, your implementation code needs to effectively be inline, i.e., in the header as you discovered.
For a fuller explanation of why it has to be this way, see the C++ FAQ Lite.
As a general rule, you must put all template members inside of the header file. Templates are compiled in an as-used basis, and hence the entire definition needs to be available wherever they are used. Putting the code in the header file will solve that problem.
The only time you can put a template definition in a CPP file is when the template will only be used within that CPP file. The reason being is that it meets the standard that the entire definition is available for compilation.
Moving the contents of node.cpp to node.h will fix the problem.
Strange Scenarios
Then again, you can also put everything in a CPP file and include the CPP file. C++ is flexible in this way. I only mention this because I've seen in done before. I actually bruised my jaw when it hit the top of my desk.
When you use node<int>, you have not most likely included node.cpp. Therefore the compiler cannot instantiate the node<int>::node<int> constructor. Usually you put all the template code, including all the implementations of the methods, in the header file, or something included from it.
The commonly accepted practice is to put all of the implementation in the .h file, so that the classes can be generated from the template as needed.
If you know ahead of time which types your template will be instantiated with, you might be able to cheat a little. Just make sure your .cpp includes a use case for each type and method you will need. It is important that the use case come after the template code. E.g. for "node.cpp", use
#include "node.h"
template<class T>
node<T>::node (T data) {
_data = data;
}
void dummy(void)
{
node<int> intnode(0);
node<double> doublenode(0.0);
}
// You can put templates declaration in header and definition in source
// node.h or wherever you include file gets included
extern template class node<int>;
// node.cpp or where ever source file you want to use it
// But use it only once for each type of generated class
template class node<int>;
Unless there's a call to the function, the compiler won't output any code and the linker won't find it.
You should put the function in the header where it belongs.
implicit instantiation is turned off, you need
template class node<int>;
somewhere in your code (node.cpp maybe)
EDIT: bad answer, it's probably not the case.