My network code uses template specialization to serialize types that can not simply be copied. I defined a general template
template<typename T> struct TypeHandler
that handles all types that can be transported by a simple memcpy
and then I define specializations for all other types. The problem now is that I have a file with multiple such specializations and if I compile the code with Visual Studio everything works fine. But with gcc all template specializations in that file get used with the exception of
template<> struct TypeHandler<uint32_t>
which variable length encodes the integer to save space.
Namespaces are the same for all TypeHandler versions and they are even in the same file. But for some reason gcc decides to use the generalized version and I don't really know why.
EDIT:
It seems that gcc uses the instantiation of TypeHandler from an other project that this one links against but doesnt have a specialization for uint32_t even so it transmits uint32_t fields. GCC doesnt give me any error though. How can i tell gcc to use the specialization like Visual Studio does ?
EDIT2:
managed to generate an SSCCE http://netload.in/dateiz3R4eTVqi3/src.tar.gz.htm
the bug here is the other way around but well.
EDIT3:
fixed filesize : http://netload.in/dateixP6iOvc6bD/src.zip.htm
Minimized to:
test1.cpp
:
#include <iostream>
#include <stdint.h>
template<typename T>
struct TypeHandler
{
void Print() { std::cout << "base" << std::endl; }
};
void test1()
{
std::cout << "p1" << std::endl;
TypeHandler<uint32_t>().Print();
}
test2.cpp
:
#include <iostream>
#include <stdint.h>
template<typename T>
struct TypeHandler
{
void Print() { std::cout << "base" << std::endl; }
};
template<>
struct TypeHandler<uint32_t>
{
void Print() { std::cout << "int" << std::endl; }
};
void test2()
{
std::cout << "p2" << std::endl;
TypeHandler<uint32_t>().Print();
}
main.cpp
:
void test1();
void test2();
int main(){
test1();
test2();
}
On Windows/MinGW 4.8.2, compiling with g++ test1.cpp test2.cpp main.cpp -o test
and running produces
p1
base
p2
base
while using g++ test2.cpp test1.cpp main.cpp -o test
produces
p1
int
p2
int
This is a straightforward standard violation causing undefined behavior. You can't explicitly specialize the same template in one translation unit but not the other. The explicit specialization is not visible in test1.cpp
, causing the compiler to generate an implicit instantiation from the base template. So you get two TypeHandler<uint32_t>
specializations, and in this instance it appears that the linker decided to pick the one from the first object file it saw. From §14.7.3 [temp.expl.spec]/p6 of the standard (emphasis mine):
If a template, a member template or a member of a class template is
explicitly specialized then that specialization shall be declared
before the first use of that specialization that would cause an
implicit instantiation to take place, in every translation unit in
which such a use occurs; no diagnostic is required. If the program
does not provide a definition for an explicit specialization and
either the specialization is used in a way that would cause an
implicit instantiation to take place or the member is a virtual member
function, the program is ill-formed, no diagnostic required. An
implicit instantiation is never generated for an explicit
specialization that is declared but not defined.
Also, obligatory quote of the next paragraph (emphasis mine):
The placement of explicit specialization declarations for function
templates, class templates, member functions of class templates,
static data members of class templates, member classes of class
templates, member enumerations of class templates, member class
templates of class templates, member function templates of class
templates, member functions of member templates of class templates,
member functions of member templates of non-template classes, member
function templates of member classes of class templates, etc., and the
placement of partial specialization declarations of class templates,
member class templates of non-template classes, member class templates
of class templates, etc., can affect whether a program is well-formed
according to the relative positioning of the explicit specialization
declarations and their points of instantiation in the translation unit
as specified above and below. When writing a specialization, be
careful about its location; or to make it compile will be such a trial
as to kindle its self-immolation.