Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

G++ generates code for unused template specializations?

In a bit of serialization code for a project I'm working on I have a type whose size is compiler dependent. In order to deal with this, I decided to use a template specialization, which works great. Everything is resolved at compile time. The code looks a little bit like this (not the real code, just an example):

template <int size>
void
special_function()
{
     std::cout << "Called without specialization: " << size << std::endl;
}

template <>
void
special_function<4>()
{
     std::cout << "dword" << std::endl;
}

template <>
void
special_function<8>()
{
     std::cout << "qword" << std::endl;
}

int
main()
{
     special_function<sizeof(int)>();
     return 0;
}

On my 32-bit system, executing the above program outputs dword, as expected. But the whole point of doing it this way and not just doing if (sizeof(int) == 4) { ... } else if ... is that I had hoped that the compiler would only produce code for the appropriate function. Since special_function<4> is the only one called in this program, I expected it to be the only one generated by the compiler (gcc 4.1.2 in this case, on x86 Linux).

But that is not the observed behavior.

While it indeed works, the code for each template specialization is generated despite not being ever used. The generic definition is not generated, however.

I should mention that this is a one-step compilation, not a compilation into intermediary object files followed by a link. In that case it would seem natural to defer dead code removal to the link stage, and I know that linkers are not always terribly good at this.

Does anyone know what is going on? Is there a subtlety of template specialization I'm missing here? Lord knows the devil is in the details with C++.

EDIT: Since it's been mentioned, this behavior occurs with both -O3 and -Os.

EDIT2: Rob below suggested putting the functions in an anonymous namespace. Doing so and compiling with any level of optimization does indeed remove the dead code, which is good. But I was curious, so I tried doing the same with the following program:

namespace {
void foo() { std::cout << "Foo!" << std::endl; }
void bar() { std::cout << "Bar!" << std::endl; }
}

int
main()
{
       foo();
       return 0;
}

The idea here is see whether or not Rob's solution is actually related to template specializations. As it turns out, the above code compiled with optimizations turned on elides the unused definition of bar() from the executable. So it seems that while his answer solves my immediate problem, it doesn't explain why template specializations that aren't used are compiled at all.

Does anyone know of a relevant snippet from the standard that would explain this? I always thought templates were generated only on use, but perhaps this is not so for full specializations ...

like image 547
808140 Avatar asked Jan 30 '13 16:01

808140


People also ask

What is template specialization used for?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

What is explicit template specialization?

Explicit (full) specializationAllows customizing the template code for a given set of template arguments.

What does template <> mean?

A template is a form, mold or pattern used as a guide to make something.


2 Answers

The template specializations in your example are functions with external linkage. The compiler cannot know that they won't be called from another translation unit.

On my g++ 4.7.2 Ubuntu system, placing the templates into an anonymous namespace and compiling with -O3 prevented the unused function from being generated.

Similarly, declaring the function template static had the desired effect.

like image 174
Robᵩ Avatar answered Jan 04 '23 13:01

Robᵩ


This is a peculiar problem. I looked into it a little, and this issue is unrelated to template specialization. I guess g++ doesn't, by default, strip unused symbols. This makes sense in case you later want to link your output to another program.

However, there are command line options that you can use to strip unused symbols. For details, see this post:

How to remove unused C/C++ symbols with GCC and ld?

but also see here

Using GCC to find unreachable functions ("dead code")

and here

Dead code detection in legacy C/C++ project

Just to try this out, I modified the code as follows:

#include <iostream>

void junk_function() {
    std::cout<<"test" << std::endl;    
}

template <int size>
void special_function()
{
     std::cout << "Called without specialization: " << size << std::endl;
}

template <>
void special_function<4>()
{
     std::cout << "dword" << std::endl;
}

template <>
void special_function<8>()
{
     std::cout << "qword" << std::endl;
}

int main()
{
     special_function<sizeof(int)>();
     return 0;
}

Then stored this code to sp.cpp. First,

g++ -Os sp.cpp -o sp
nm sp

and got this (note, I removed a bunch of symbols for readability):

0804879a T _Z13junk_functionv
080487b8 T _Z16special_functionILi4EEvv
080487f5 T _Z16special_functionILi8EEvv

Seems the two unused symbols are there. I also tried -O1, -O2, -O3, and got the same.

Next:

g++ -Os -fdata-sections -ffunction-sections sp.cpp -o sp -Wl,--gc-sections
nm sp

and got this:

0804875a T _Z16special_functionILi4EEvv

That's it. So it looks like you just need to pass the right arguments in to tell g++ to strip unused symbols. On mac, I guess they have the -dead_strip option, but I don't know why it doesn't work in g++ (even though it is mentioned in the man pages. Admittedly, I didn't dig into this, so there may be a fine print that I missed).

I think Visual C++'s linker strips by default when you link, but I didn't test. Maybe someone else can chime in.

like image 37
thang Avatar answered Jan 04 '23 15:01

thang