Sunday, 28 August 2016

class - Splitting templated C++ classes into .hpp/.cpp files--is it possible?



I am getting errors trying to compile a C++ template class which is split between a .hpp and .cpp file:



$ g++ -c -o main.o main.cpp  
$ g++ -c -o stack.o stack.cpp

$ g++ -o main main.o stack.o
main.o: In function `main':
main.cpp:(.text+0xe): undefined reference to 'stack::stack()'
main.cpp:(.text+0x1c): undefined reference to 'stack::~stack()'
collect2: ld returned 1 exit status
make: *** [program] Error 1


Here is my code:




stack.hpp:



#ifndef _STACK_HPP
#define _STACK_HPP

template
class stack {
public:
stack();
~stack();

};
#endif


stack.cpp:



#include 
#include "stack.hpp"

template stack::stack() {

std::cerr << "Hello, stack " << this << "!" << std::endl;
}

template stack::~stack() {
std::cerr << "Goodbye, stack " << this << "." << std::endl;
}


main.cpp:




#include "stack.hpp"

int main() {
stack s;

return 0;
}


ld is of course correct: the symbols aren't in stack.o.




The answer to this question does not help, as I'm already doing as it says.
This one might help, but I don't want to move every single method into the .hpp file—I shouldn't have to, should I?



Is the only reasonable solution to move everything in the .cpp file to the .hpp file, and simply include everything, rather than link in as a standalone object file? That seems awfully ugly! In that case, I might as well revert to my previous state and rename stack.cpp to stack.hpp and be done with it.


Answer



It is not possible to write the implementation of a template class in a separate cpp file and compile. All the ways to do so, if anyone claims, are workarounds to mimic the usage of separate cpp file but practically if you intend to write a template class library and distribute it with header and lib files to hide the implementation, it is simply not possible.



To know why, let us look at the compilation process. The header files are never compiled. They are only preprocessed. The preprocessed code is then clubbed with the cpp file which is actually compiled. Now if the compiler has to generate the appropriate memory layout for the object it needs to know the data type of the template class.



Actually it must be understood that template class is not a class at all but a template for a class the declaration and definition of which is generated by the compiler at compile time after getting the information of the data type from the argument. As long as the memory layout cannot be created, the instructions for the method definition cannot be generated. Remember the first argument of the class method is the 'this' operator. All class methods are converted into individual methods with name mangling and the first parameter as the object which it operates on. The 'this' argument is which actually tells about size of the object which incase of template class is unavailable for the compiler unless the user instantiates the object with a valid type argument. In this case if you put the method definitions in a separate cpp file and try to compile it the object file itself will not be generated with the class information. The compilation will not fail, it would generate the object file but it won't generate any code for the template class in the object file. This is the reason why the linker is unable to find the symbols in the object files and the build fails.




Now what is the alternative to hide important implementation details? As we all know the main objective behind separating interface from implementation is hiding implementation details in binary form. This is where you must separate the data structures and algorithms. Your template classes must represent only data structures not the algorithms. This enables you to hide more valuable implementation details in separate non-templatized class libraries, the classes inside which would work on the template classes or just use them to hold data. The template class would actually contain less code to assign, get and set data. Rest of the work would be done by the algorithm classes.



I hope this discussion would be helpful.


No comments:

Post a Comment

c++ - Does curly brackets matter for empty constructor?

Those brackets declare an empty, inline constructor. In that case, with them, the constructor does exist, it merely does nothing more than t...