Using Export Keyword with Templates

Using export keyword with templates

Attention: This answer is about the historical use of export pre-C++20; C++20 repurposes the keyword for use in modules.

First of all: most compilers (including gcc, Clang and Visual Studio) do not support the export keyword.

It has been implemented in a single front-end: the EDG front-end, and thus only the compilers that use it (Comeau and icc) support this feature. The feedback from the implementers at EDG was extremely simple: it took us time, was extremely complicated, we recommend not to implement it (1), as a consequence it has been dropped in C++0x.

Now, the standard allows (and this is implemented by at least gcc):

  • to declare a specialized version of a template function in a header
  • to define this specialization in a single source file

and to have it behave as you'd expect from a regular function.

Note: as Johannes points out in a comment, if a full specialization of a function is defined in a header, it must be marked as inline otherwise the linker will complains.

EDIT:

(1) Finally found my reference Why can't we afford export (PDF) by Tom Plum, reviewed by Steve Adamczyk, John Spicer, and Daveed Vandevoorde of Edison Design Group who originally implemented it in the EDG front end.

Solve this Error ,while using 'export' keyword in templates?

Looks like your compiler doesn't support separate template compilation. It is a common practice not use separate compilation with templates and distribute templates in header files. Besides that, I spotted several issues.

  1. Remove export keyword from the declaration. This should take care of call of overloaded ‘min(int, int)’ is ambiguous error message. A template may be defined as exported only once in a program.
  2. X is defined twice. Why?

P.S. I've never seen any code which uses exported templates. A while ago, when I was learning C++, every compiler I tried did not support exported templates. No wonder it's going to be deprecated from C++.

C++20 modules export template instantiation

Modules affect 2 things: the scope of names and the reach-ability of declarations. Both of these only matter if they are within the purview of a module (ie: in an imported module interface TU and not being in the global module fragment).

Names declared in the purview of a module can be used outside of that module only if they are exported by that module. In the case of an explicit template instantiation, the template itself is already exported, so users outside of the module can already use the name.

However, an explicit template instantiation definition is also a declaration. And modules control the reach-ability of declarations. The thing is, the rules of reach-ability for a declaration don't actually care about export:

A declaration D is reachable if, for any point P in the instantiation context ([module.context]),

  • D appears prior to P in the same translation unit, or
  • D is not discarded ([module.global.frag]), appears in a translation unit that is reachable from P, and does not appear within a private-module-fragment.

[ Note: Whether a declaration is exported has no bearing on whether it is reachable. — end note ]

Emphasis added.

These rules only care about which TUs have been imported (and whether the declaration is in a global/private module fragment).

Therefore, if the primary template declaration was exported, an explicit template instantiation (that is not in the global/private module fragment) in one of the imported module files is reach-able by any code that imports it.

So it doesn't matter if you export an explicit template instantiation. If the primary template was already exported, its name could already be used, so the only thing that matters is if the explicit template instantiation is visible.

So your #1 and 2 are functionally equivalent. And its best not to export something you don't need to.


As for the behavior of extern template, that's interesting.

While extern normalize denotes external linkage, this does not apply to extern template. So we don't have linkage problems. And since the extern template declaration is reach-able by importers of the module (as previously stated), they'll see it and respect it.

So the only question is whether the explicit definition in your "mod_impl.cpp" is also reach-able. Except that's not a question because only the declaration part of a definition is ever "reach-able". That is, reach-ability only matters for a declaration.

The explicit instantiation definition is in a different TU. Therefore, it will only be instantiated in that TU; code which imports the module reaches only the declaration. And therefore, it won't instantiate the template.

So yes, you can perform extern template gymnastics (though again, export doesn't matter). But it's no different than just putting the explicit instantiation in your module interface, and it's way cleaner to do that.

Cannot export template function

C++ does not really support the separate compilation of template code - you need to put the definition of the template in a header file.

What is the best explanation for the export keyword in the C++0x standard?

Although Standard C++ has no such requirement, some compilers require that all function templates need to be made available in every translation unit that it is used in. In effect, for those compilers, the bodies of template functions must be made available in a header file. To repeat: that means those compilers won't allow them to be defined in non-header files such as .cpp files. To clarify, in C++ese this means that this:

// ORIGINAL version of xyz.h
template <typename T>
struct xyz
{
xyz();
~xyz();
};

would NOT be satisfied with these definitions of the ctor and dtors:

// ORIGINAL version of xyz.cpp
#include "xyz.h"

template <typename T>
xyz<T>::xyz() {}

template <typename T>
xyz<T>::~xyz() {}

because using it:

// main.cpp
#include "xyz.h"

int main()
{
xyz<int> xyzint;

return 0;
}

will produce an error. For instance, with Comeau C++ you'd get:

C:\export>como xyz.cpp main.cpp
C++'ing xyz.cpp...
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86
Copyright 1988-2004 Comeau Computing. All rights reserved.
MODE:non-strict warnings microsoft C++

C++'ing main.cpp...
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86
Copyright 1988-2004 Comeau Computing. All rights reserved.
MODE:non-strict warnings microsoft C++

main.obj : error LNK2001: unresolved external symbol xyz<T1>::~xyz<int>() [with T1=int]
main.obj : error LNK2019: unresolved external symbol xyz<T1>::xyz<int>() [with T1=int] referenced in function _main
aout.exe : fatal error LNK1120: 2 unresolved externals

because there is no use of the ctor or dtor within xyz.cpp, therefore, there is no instantiations that needs to occur from there. For better or worse, this is how templates work.

One way around this is to explicitly request the instantiation of xyz, in this example of xyz<int>. In a brute force effort, this could be added to xyz.cpp by adding this line at the end of it:

template xyz<int>;

which requests that (all of) xyz<int> be instantiated. That's kind of in the wrong place though, since it means that everytime a new xyz type is brought about that the implementation file xyz.cpp must be modified. A less intrusive way to avoid that file is to create another:

// xyztir.cpp
#include "xyz.cpp" // .cpp file!!!, not .h file!!

template xyz<int>;

This is still somewhat painful because it still requires a manual intervention everytime a new xyz is brought forth. In a non-trivial program this could be an unreasonable maintenance demand.

So instead, another way to approach this is to #include "xyz.cpp" into the end of xyz.h:

// xyz.h

// ... previous content of xyz.h ...

#include "xyz.cpp"

You could of course literally bring (cut and paste it) the contents of xyz.cpp to the end of xyz.h, hence getting rid of xyz.cpp; it's a question of file organization and in the end the results of preprocessing will be the same, in that the ctor and dtor bodies will be in the header, and hence brought into any compilation request, since that would be using the respective header. Either way, this has the side-effect that now every template is in your header file. It could slow compilation, and it could result in code bloat. One way to approach the latter is to declare the functions in question, in this case the ctor and dtor, as inline, so this would require you to modify xyz.cpp in the running example.

As an aside, some compilers also require that some functions be defined inline inside a class, and not outside of one, so the setup above would need to be tweaked further in the case of those compilers. Note that this is a compiler issue, not one of Standard C++, so not all compilers require this. For instance, Comeau C++ does not, nor should it. Check out http://www.comeaucomputing.com/4.0/docs/userman/ati.html for details on our current setup. In short, Comeau C++ supports many models, including one which comes close to what the export keyword's intentions are (as an extension) as well as even supporting export itself.

Lastly, note that the C++ export keyword is intended to alleviate the original question. However, currently Comeau C++ is the only compiler which is being publicized to support export. See http://www.comeaucomputing.com/4.0/docs/userman/export.html and http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt for some details. Hopefully as other compilers reach compliance with Standard C++, this situation will change. In the example above, using export means returning to the original code which produced the linker errors, and making a change: declare the template in xyz.h with the export keyword:

// xyz.h

export
// ... ORIGINAL contents of xyz.h ...

The ctor and dtor in xyz.cpp will be exported simply by virtue of #includeing xyz.h, which it already does. So, in this case you don't need xyztir.cpp, nor the instantiation request at the end of xyz.cpp, and you don't need the ctor or dtor manually brought into xyz.h. With the command line shown earlier, it's possible that the compiler will do it all for you automatically.



Related Topics



Leave a reply



Submit