Linking/compile time concerning static template libraries
Asked Answered
I

3

5

It seems to be a common convention not to use source files for template based classes (STL and boost) and to put the implementation into the header as well. I assume that this will increase the time it takes to compile the source files that include the header drastically compared to the classic separation between declaration and implementation in header and source files. The reason why this is done is probably due to the fact that you would have to tell the compiler in the source file which templates to use, which will probably result in a bloated .a file.

Assuming the linker also requires more time as the library grows, which approach would be faster in terms of the time it takes to compile a source file that includes the library header?

1. Not using a .cpp file and put the entire class, including the implementation, into the header

//foo.hpp
template <class T>
class Foo
{
public:
    Foo(){};
    T bar()
    {
        T* t = NULL;
        //do stuff
        return *t;
    }
};

or

2. Explicitly compiling the template for various types inside the source file of the library itself

//foo.h
template <class T>
class Foo
{
public:
    Foo(){};
    T bar();
};

//foo.cpp
template <class T>
T Foo<T>::bar()
{
    T* t = NULL;
    //do stuff
    return *t;
}

template class Foo<int>;
template class Foo<float>;
template class Foo<double>;
template class Foo<long long>;
Ivyiwis answered 16/10, 2012 at 15:17 Comment(0)
B
4

The key issue with templates is that the compiler won't know for which template arguments a template will be used. The only time the compiler knows a template is used with a specific set of arguments is when it sees the template used and the compiler will instantiate the template at this point. As a result the code is often put into the header so the compiler can instantiate the template on the user's behalf when it is used.

Alternatively, the author of a template can tell the compiler that a template is used with a specific list of template arguments and just instantiate them explicitly. In this case, the template definition can go into a source file (or, more likely, a special header not generally included by users). The problem with this approach is that the author of the template code doesn't necessarily know which instantiations are needed.

In C++ 2011 there is also a middle ground: It is possible to tell the compiler that certain instantiations are already created by declaring a specialization as extern. This way, the compiler knows that it doesn't need to instantiate the template with certain arguments but if other arguments are used it knows that it needs to create them. For example, the standard C++ library has std::basic_string and it can predict that instantiations for char and wchar_t are likely to be used and can put them into the library, declaring the instantiations as extern. However, having the code readily available makes it viable to use std::basic_string<user_type> with user defined types.

Going forward we hope to get a module system but right now nobody really knows how such a system should really work. The compiler implementers who are interested in the topic have a group to think about modules and it is likely that a system like this may help with compile times for templates.

Barytes answered 16/10, 2012 at 15:48 Comment(2)
I know most of this already. I'm just wondering which approach takes less time to compile and link for the user (given that I know which types are needed).Ivyiwis
If you can avoid having code instantiated automatically in multiple translation units it will generally be faster to compile the code with preinstantiated templates, especially when using optimziation. Link times may go down as well although this somewhat depends on linker. With moving the IOStream and locales library out of headers I could improve the compiles dramatically! However, I haven't tried whether the same effect can be achieved using extern templates.Kantian
L
4

Normally, the compiler generates code once for a particular compilation unit and the linker then ensures that other compilation units can access the variables and functions.

When it comes to templates, this is no longer the case. The compiler is not able to generate code for a template before a concrete instance of the template is instantiated. We therefore instantiate the template in every compilation unit and the only way to do this without copy and paste is to place all the template code in the header file.

The linker also then has to be template aware and reconcile multiple instances of the same object.

C++11 does help a bit with this scenario where one can declare:

template class MyTemplate<MyType>;

in a single C++ file and then use:

extern template class MyTemplate<MyType>;

The latter does not instantiate the template in the current compilation unit but will get the linker to link to one already defined.

see here for more detail

Laze answered 16/10, 2012 at 15:34 Comment(0)
B
4

The key issue with templates is that the compiler won't know for which template arguments a template will be used. The only time the compiler knows a template is used with a specific set of arguments is when it sees the template used and the compiler will instantiate the template at this point. As a result the code is often put into the header so the compiler can instantiate the template on the user's behalf when it is used.

Alternatively, the author of a template can tell the compiler that a template is used with a specific list of template arguments and just instantiate them explicitly. In this case, the template definition can go into a source file (or, more likely, a special header not generally included by users). The problem with this approach is that the author of the template code doesn't necessarily know which instantiations are needed.

In C++ 2011 there is also a middle ground: It is possible to tell the compiler that certain instantiations are already created by declaring a specialization as extern. This way, the compiler knows that it doesn't need to instantiate the template with certain arguments but if other arguments are used it knows that it needs to create them. For example, the standard C++ library has std::basic_string and it can predict that instantiations for char and wchar_t are likely to be used and can put them into the library, declaring the instantiations as extern. However, having the code readily available makes it viable to use std::basic_string<user_type> with user defined types.

Going forward we hope to get a module system but right now nobody really knows how such a system should really work. The compiler implementers who are interested in the topic have a group to think about modules and it is likely that a system like this may help with compile times for templates.

Barytes answered 16/10, 2012 at 15:48 Comment(2)
I know most of this already. I'm just wondering which approach takes less time to compile and link for the user (given that I know which types are needed).Ivyiwis
If you can avoid having code instantiated automatically in multiple translation units it will generally be faster to compile the code with preinstantiated templates, especially when using optimziation. Link times may go down as well although this somewhat depends on linker. With moving the IOStream and locales library out of headers I could improve the compiles dramatically! However, I haven't tried whether the same effect can be achieved using extern templates.Kantian
A
1

There are already two good answers, so I will just write briefly.
Templated code cannot be compiled without concrete types, which is why you can't do a classic "compile-and-link".
A template function or class is not complete, in the sense that not all types are resolved, until it's specific usage in the code (where a certain type replaces the template).
For this reason, templates are put in the header files and cannot be compiled independent of specific usage.

Abutilon answered 16/10, 2012 at 15:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.