export lets you separate the declaration (ie. header) from the definition (ie. the code) when you write your template classes.
You can export an entire C++ class by placing the __declspec(dllexport) before the class name, or you can export a single method by placing __declspec(dllexport) before the method name. Make class methods static and public. The C/C++ DLL Adapter does not allow you to call public non-static methods.
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.
See here and here for Herb Sutter's treatment of the subject.
Basically: export has been implemented in only one compiler - and in that implementation, export actually increases the coupling between template definition and declaration, whereas the only point in introducing export was to decrease this coupling.
That's why most compilers don't bother. I would have thought they would have just removed export from the language in C++0x, but I don't think they did. Maybe some day there will be a good way to implement export that has the intended use.
See this explanation for its use
Quite a few compilers don't support it either because it's too new or in the case of gcc - because they disaprove.
This post describes standard support for many compilers. Visual Studio support for new C / C++ standards?
To put it simply:
export
lets you separate the declaration (ie. header) from the definition (ie. the code) when you write your template classes. If export
is not supported by your compiler then you need to put the declaration and definition in one place.
Export is a feature that introduces a circular dependency between linker and compiler. As others noted, it allows one translation unit to contain the definition of a template used in another. The linker will be the first to detect this, but it needs the compiler for the instantiation of the template. And this involves real hard work, like name lookup.
Comeau introduced it first, about 5 years ago IIRC. It worked quite well on the first beta release I got. Even testcases like A<2> using B<2> using A<1> using B<1> using A<0>, worked, if templates A and B came from different TU's. Sure, the linker was repeatedly invoking the compiler, but all name lookups worked OK. Instantiation A<1> found names from A.cpp that were invisible in B.cpp.
Standard Features Missing From VC++ 7.1. Part II: export
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With