I am building a library that handles several different types of binary "languages", which has a "Processor" type for each language. The library builds fine, and I've narrowed down the issue so far to the following template code:
// Processor.h
template <class A, class B = DefaultProcessor>
class Processor
{
public:
// Default constructor.
Processor(void);
// Constructor from DefaultProcessor pointer type.
Processor(B* pB);
virtual ~Processor(void){};
// Dereferencing operator
A* operator->(void) const;
private:
A* pRawPointer;
};
// Processor.cpp
template <class A, class B>
A* Processer<A, B>::operator->(void) const
{
if (nullptr == pRawPointer)
{
throw();
}
return pRawPointer;
}
// Constructor from DefaultProcessor pointer type.
template <class A, class B>
Processor<A, B>::Processor(B* pB)
: pRawPointer(dynamic_cast<A*>(pB))
{
}
I have dozens of different classes it supports, and in my library, I have a long list of explicit instantiations:
template class Processor<CustomType1>;
template class Processor<CustomType2>;
template class Processor<CustomType3>;
template class Processor<CustomType1, CustomType2>;
template class Processor<CustomType4>;
template class Processor<CustomType5>;
template class Processor<CustomType6>;
When I attempt to build an application that links against my library, I encounter the following errors when compiling via g++ -Wall -std=c++11
, but doesn't have any issues building in Visual Studio 2015:
undefined reference to `Processor<CustomType4, DefaultProcessor>::Processor(DefaultProcessor*)'
undefined reference to `Processor<CustomType4, DefaultProcessor>::operator->() const'
undefined reference to `Processor<CustomType5, DefaultProcessor>::Processor(DefaultProcessor*)'
undefined reference to `Processor<CustomType5, DefaultProcessor>::operator->() const'
It's almost as if the explicit instantiations aren't being completely generated when building in Linux. I've tried explicitly instantiating in the library via:
template class Processor<CustomType4, DefaultProcessor>;
template class Processor<CustomType5, DefaultProcessor>;
This just causes the library to fail to build due to duplicate explicit instantiations.
What would cause this issue to arise solely in Linux builds?
Thank you.
Your template does not define a constructor that accepts DefaultProcessor *
as a parameter, so that is clearly undefined.
Your explicit instantiations have to be present in the same file that defined the implementation of your default constructor and operator->
. Otherwise, those methods won't be instantiated.
I tested your code by defining some dummy classes at the top of a file that defined your template.
struct DefaultProcessor { virtual ~DefaultProcessor() {} };
struct CustomType2 : DefaultProcessor {};
struct CustomType1 : CustomType2 {};
struct CustomType3 : DefaultProcessor {};
struct CustomType4 : DefaultProcessor {};
struct CustomType5 : DefaultProcessor {};
struct CustomType6 : DefaultProcessor {};
At the bottom of the C++ file that had your template method definitions, I added the explicit definitions. I then compiled the code like this:
g++ -fPIC -std=c++0x -c t.cc
g++ -shared -o t.so t.o
To observe if the instantiations stuck, I used nm
and c++filt
. Here are the symbols that contained CustomType1
:
0000000000002a06 W Processor<CustomType1, CustomType2>::Processor(CustomType2*)
0000000000002a06 W Processor<CustomType1, CustomType2>::Processor(CustomType2*)
0000000000002a9c W Processor<CustomType1, CustomType2>::~Processor()
0000000000002a66 W Processor<CustomType1, CustomType2>::~Processor()
0000000000002a66 W Processor<CustomType1, CustomType2>::~Processor()
00000000000026dc W Processor<CustomType1, DefaultProcessor>::Processor(DefaultProcessor*)
00000000000026dc W Processor<CustomType1, DefaultProcessor>::Processor(DefaultProcessor*)
0000000000002772 W Processor<CustomType1, DefaultProcessor>::~Processor()
000000000000273c W Processor<CustomType1, DefaultProcessor>::~Processor()
000000000000273c W Processor<CustomType1, DefaultProcessor>::~Processor()
0000000000002ac2 W Processor<CustomType1, CustomType2>::operator->() const
0000000000002798 W Processor<CustomType1, DefaultProcessor>::operator->() const