Saturday, January 24, 2009

Inline specializations and multiple inclusions

Recently, I came across a problem being faced by an individual on the forums. He had the code like this:

[CODE]
//file: header.h
#ifndef HEADER_H
#define HEADER_H

#include <iostream>

template<class T>
void foo(T val){
    std::cout << "foo<T>("<<val<<")\n";
}
template<>
inline void foo(double val){
    std::cout << "foo<double>(" << val <<")\n";
}

#endif

//file: main.cpp
#include "header.h"

int main(){
    double x=4;
    foo(x);
    return 0;
}

//file: other.cpp
#include "header.h"
void somecode(){}

Just to summarize the above code, there is a header file (header.h) that contains the template functions and its specialization on type 'double' for template type parameter declared inline. And that header is being included into 2 implementation files (main.cpp & other.cpp)

This code compiled fine for him but as soon as he removed the inline keyword, it started to given him linker error for multiple definitions of the foo<double> function. We might start wondering what is it between templates and inline that might be causing this? Something and nothing. What happens is explained below:

The linker error seen is not specific to templates or specializations. One will face the same problem if they turned foo into a non-template function. C++ functions have external linkage hence they must be implemented in just one .cpp file (implementation file). They can, however, be declared multiple times. When we provide the implementation in the header file instead of an implementation file and that header is being included multiple implementation files (main.cpp and other.cpp), we have multiple definitions and hence, rightly the linkage error. Putting the implementation into the header file was what we were forced to do when using a template function because we need to put the implementation in the header file as well as the declaration since 'export' doesn't work except for just with the Comeau compiler. But specializations are a different case than regular templates. We don't face such issues with regular templates (not their specializations) because the instantiation rules guarantees that there is just one copy of the function generated as and when needed. While in case of specializations, we end up with multiple definitions because of them being included into multiple implementation files.

Now, with inline functions, we provide the implementation in the header file so as to be visible to the compilation point where they are used. They become inline in the final executable or not is irrelevant. You just follow the rule to define the inline functions. There will be only one copy of them.

Coming to the solutions to the above linker error, below are the two simplest ways:

1. Make the specialization as inline (as in original code, in case, someone facing this issue didn't have it in the first place)

2. Declare it not as a specialization but just as an overloaded foo function and declare it in the header (header.h) and provide implementation in an implementation file (for that matter any one implementation main.cpp/other.cpp or a .cpp of its own for header.h templates' specializations just making sure we don't do it twice).

References:

1. Codeguru thread where I came across this - "inline" and linking errors
2. C++ FAQ Lite on Inline Functions

5 comments:

Anonymous said...

You didn't explain why removing the inline keyword resulted in a linker error and adding it make the linker okay.
-Seb

abnegator said...

Hi Seb,

The reasons for those are explained above in the paragraph starting: "The linker error seen is not specific to templates ...." and the next one.

The link to codeguru thread and C++ FAQ Lite seem to have disappeared too. Will try to fix them as soon as possible.

Cheers,
Abhishek

Anonymous said...

Yes I got that Abhishek.
I admit I don't get why a function defined inline in a header is okay while its non-inline version breaks.
Both have external linkage I believe. And the compiler is not required to implement it as "inline" (just a hint).
In this case the function becomes like a non-line function with external linkage and multiple definition can occurs, can't it ?

I'd like to understand how the compilers handle it. I think this problem lurks in your post when you say "They become inline in the final executable or not is irrelevant"
If you have more insights I'd be glad to hear from you.

Buy the way it's an interesting blog. I hope to read more.
-Seb

cheap shoes said...

This blog has been very helpful to me! I look forward on learning new ideas and stuffs in here, I will bookmark! Thanks for this.

symen said...

This is a very interesting blogsite. I also teach software engineering in Asia.I'd like to understand how the compilers handle it. I think this problem lurks in your post when you say...logo design