Monday, September 19, 2016

Multiple instances of template classes on Windows

A common problem on Windows is that the linker complains over multiple instances of classes, most often template classes.
The reason for this is that normally, template classes are instantiated where they are used, and their binary code is not exported, i.e. they are not included in the content-list of the library they are baked into.

On Windows, an exported class is required to have all functions compiled and ready in the  library itself, including functions of inherited classes. All these functions are also exported, i.e. added to the content list of the library. Consider the following code(s):


In library Basic (a.h)
template <class T>
class A
{
    public:
                    A();
    int             compute() const;
};

As this is a template class, it is not exported, and no instances are added to the Basic.dll's list of contents.

In library "General" (b.h)

mExpClass(General) B : public A<int>
{
    ...
};

Here the compiler will export all functions in B and all functions in A<int>. This means that A<int>::compute() will be present on General.dll's list of contents. So far so good. The problem does however come if another library does the same:


In library "Well" (c.h)

mExpClass(General) C : public A<int>
{
    ...
};

As the compiler does not know that A<int> is already available in "General", it will instantiate all functions of A<int> into Well.dll just as when the "General" library was compiled. Hence, the linker will complain that there are two versions available, the one in Well.dll and the one from General.dll.

In OpendTect, we have solved this problem by explicitly telling that template classes that are instantiated in each library. This is done in the *export.h files. Everyone who is using a class from say the "General" library will include one or more headers from the General library. These files in turn include the "generalmod.h" header, which in turn includes the "generalexport.h" header.

generalexport.h looks like this:

#ifdef do_import_export
# include <a.h>

mExportTemplClassInst( General ) A<int>;

#endif

Hence, everyone who uses any class from General, will include this. The two macros essentially tell every user of General that A<int> is available in the "General" library. In the example above, the compiler of the class "C" would not create a new instance of A<int> as it would know that it is already done in the "General" library.

If you get problems with multiple instances, figure out in which module the template class is instantiated first, and add it to the export header of that module.

Thursday, September 15, 2016

Use of #pragma once

Fellow developers,

a short note that I have just committed a large change where we go from using double-include protection using

#ifndef filename_h
#define filename_h

...

#endif

But using the simpler, and less error prone

#pragma once

in the top of the file. The ifdef in the end of the file goes away.

Cheers,


Kristofer