Search code examples
c++linuxtemplatesc++11explicit-instantiation

Some explicit instantiations not being generated in G++


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.


Solution

  • 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