2021-07-04

22: Export Symbols From a DLL for Visual C++

<The previous article in this series | The table of contents of this series | The next article in this series>

Otherwise, the symbols would be invisible from the outside of the DLL. How to control '__declspec (dllexport)' and '__declspec (dllimport)'.

Topics


About: C++
About: Visual C++

The table of contents of this article


Starting Context



Target Context


  • The reader will know that Visual C++ requires symbols to be explicitly exported from any DLL in order for them to be visible from the outside of the DLL, and how to accomplish that.

Orientation


There will be an article that examines the appalling-ness of the 'inline' specifications.

There are some more articles on Visual C++ peculiarities (an "unable to match function definition to an existing declaration" error, some enigmatic errors for trying to explicitly instantiate any constructor template, the 'std::codecvt<char16_t,char,struct _Mbstatet> unresolved error, the "C2131: expression did not evaluate to a constant" error).


Main Body

Stage Direction
Hypothesizer 7 soliloquies.


1: Symbols from Dlls Are Not Resolved? . . . Why Only for Visual C++?


Hypothesizer 7
As I usually do C++ programming with GCC, I have a set of projects, a main project and several library projects, which builds fine with GCC, and now I have to build them with Visual C++.

After some incompatibilities (some of which are objectionable) were rectified, the separate compilations have at last succeeded, but the linking fails with many errors.

. . . Well, all the symbols in any DLL seem to be not recognized from the outside of the DLL.

It has turned out that Visual C++ requires the symbols to be exported explicitly, while GCC requires nothing of the sort.

And "be exported explicitly" means not a mere compilation switch, but some rather bothersome treatments (I do not understand why Visual C++ cannot be as easy as GCC is).


2: The Basics and the Complications, and My Solution


Hypothesizer 7
Although the symbols can be exported by creating a so-called "module definition file" and specifying it to the linker, without tweaking any source file (a header file or a 'cpp' file), I will take a measure of tweaking source files because creating the "module definition file" seems more tedious.

The basics of exporting any symbol is to put a qualification, '__declspec (dllexport)', at a certain place.

"at a certain place"? . . . For example, this exports the members (there is only one member in this case, though) of a class.

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	#define __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	
	#include <string>
	
	using namespace ::std;
	
	namespace theBiasPlanet {
		namespace coreUtilities {
			namespace constantsGroups {
				class __declspec (dllexport) FileNameSuffixesConstantsGroup {
					public:
						static string const c_xmlFileNameSuffix;
				};
			}
		}
	}
#endif

Note that that does not export the class itself, but its members. . . . What about the class itself? Any class itself is not any objective object, but a blueprint, so, the class itself does not need to be exported, because the class is not in the object file.

On the other hand, although I have shown only the header file, there is also a 'cpp' file that has defined the 'c_xmlFileNameSuffix' variable, which is an objective object and has to be exported.

Anyway, the complications are that as the header file is supposed to be included also into any outside artifact, I cannot write that "__declspec (dllexport)" qualification just literally there, because the outside artifact is not exporting the class members.

In fact, I have to control the qualification such that it becomes '__declspec (dllexport)' only when the header file is included into the DLL and becomes '__declspec (dllimport)' otherwise.

While I said "included into the DLL", "the DLL" must be the exact DLL that has defined the symbols, not another DLL, because the another DLL is an importer of the symbols.

So, this, specifying the 'DLL' definition for any DLL, does not work, obviously.

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	#define __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	#ifdef DLL
		#define __symbolExportingOrImportingForVisualCplusplus__ __declspec (dllexport)
	#else
		#define __symbolExportingOrImportingForVisualCplusplus__ __declspec (dllimport)
	#endif
	
	#include <string>
	
	using namespace ::std;
	
	namespace theBiasPlanet {
		namespace coreUtilities {
			namespace constantsGroups {
				class __symbolExportingOrImportingForVisualCplusplus__ FileNameSuffixesConstantsGroup {
					public:
						static string const c_xmlFileNameSuffix;
				};
			}
		}
	}
#endif

. . . So, whether the built artifact is a DLL or not is not the issue, but whether the symbol is owned by the built artifact or not is.

So, each of the definition names has to be artifact-specific like this, where the '__theBiasPlanet_coreUtilities__' definition is specified at the compile time.

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	#define __theBiasPlanet_coreUtilities_constantsGroups_FileNameSuffixesConstantsGroup_hpp__
	#ifdef __theBiasPlanet_coreUtilities__
		#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllexport)
	#else
		#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllimport)
	#endif
	
	#include <string>
	
	using namespace ::std;
	
	namespace theBiasPlanet {
		namespace coreUtilities {
			namespace constantsGroups {
				class __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ FileNameSuffixesConstantsGroup {
					public:
						static string const c_xmlFileNameSuffix;
				};
			}
		}
	}
#endif

Is that all? . . . Well, as my header file has to be good also for GCC, I have to control the qualification such that it disappears for GCC, like this, specifying the 'GCC' definition or not at the compile time.

@C++ Source Code
#ifdef GCC
	#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__
#else
	#ifdef __theBiasPlanet_coreUtilities__
		#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllexport)
	#else
		#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllimport)
	#endif
#endif

Of course, I do not need to write that same thing in multiple files, but can put that into a single header file (let it be 'theBiasPlanet/coreUtilities/visualCplusplusSpecificHeaders/VisualCplusplusSpecificDefinitions.hpp' here) and let multiple files include it, but anyway, it seems foolish that I have to do such a tedious thing in order to do just an innocent thing as exporting symbols from a DLL.


3: Exporting Template Instances Entails More Complications


Hypothesizer 7
There are more complications about exporting template instances.

Huh? Is that not just like this (NavigableLinkedMap' is a standard-map-compatible elements-order-preserved map template introduced in a previous article)?

theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp

@C++ Source Code
#ifndef __theBiasPlanet_coreUtilities_collections_NavigableLinkedMap_hpp__
	#define __theBiasPlanet_coreUtilities_collections_NavigableLinkedMap_hpp__
	
	~
	
	namespace theBiasPlanet {
		namespace coreUtilities {
			namespace collections {
				template <typename T, typename U, typename W = less <T>> class __declspec (dllexport) NavigableLinkedMap {
					~
				}
			}
		}
	}
}

No. That does not work.

Let me clarify some things.

1st, template itself (whether it is a class template or a function template) is not any objective object, but a blueprint, so, I never export any template, although I may export a template instance.

2nd, class template specialization is a class, which is still a blueprint, which is never exported.

3rd, when any function template is inlined, no function object is generated, so, there is nothing to be exported.

4th, if I let other artifacts instantiate a function template, there will no object generated in the DLL, so, there is no need to do exporting.

So, I have to think only of the cases in which the DLL instantiates a function template.

Although the instantiation can be done explicitly or implicitly, in order to export the instance, I have to do it explicitly like this, as far as I know.

'theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp'

@C++ Source Code
#include "theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.tpp"
#include "theBiasPlanet/coreUtilities/visualCplusplusSpecificHeaders/VisualCplusplusSpecificDefinitions.hpp"

using namespace ::theBiasPlanet::coreUtilities::collections;

template class __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ NavigableLinkedMap <string, int>;
template __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ pair <NavigableLinkedMap <string, int>::iterator, bool> NavigableLinkedMap <string, int>::emplace <string, int> (string && a_argument0, int && a_argument1);

Note that although 'template class __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ NavigableLinkedMap <string, int>;' looks as though instantiating the class template, no, it is really instantiating some members of the class template.

Anyway, that is only for exporting the symbols from the DLL; what should any outside artifact do in order to import the symbols?

In fact, the outside artifact should have this code.

@C++ Source Code
#include "theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp"
#include "theBiasPlanet/coreUtilities/visualCplusplusSpecificHeaders/VisualCplusplusSpecificDefinitions.hpp"

using namespace ::theBiasPlanet::coreUtilities::collections;

template class __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ NavigableLinkedMap <string, int>;
template __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ pair <NavigableLinkedMap <string, int>::iterator, bool> NavigableLinkedMap <string, int>::emplace <string, int> (string && a_argument0, int && a_argument1);

Note that I cannot include 'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.tpp', but 'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp': that is a rule for using the '__declspec (dllimport)' qualification, and in the first place, the gist of exporting the template instances from the DLL is that the 'tpp' file is not exposed to any end user of the DLL.

Although I could newly create a 'cpp' file that directly contains that code in the outside artifact, I make the source file in the DLL like this and let the outside artifact include it.

'theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp'

@C++ Source Code
#ifdef __theBiasPlanet_coreUtilities__
	#include "theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.tpp"
#else
	#include "theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp"
#endif
#include "theBiasPlanet/coreUtilities/visualCplusplusSpecificHeaders/VisualCplusplusSpecificDefinitions.hpp"

using namespace ::theBiasPlanet::coreUtilities::collections;

template class __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ NavigableLinkedMap <string, int>;
template __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ pair <NavigableLinkedMap <string, int>::iterator, bool> NavigableLinkedMap <string, int>::emplace <string, int> (string && a_argument0, int && a_argument1);

And I create a source file like this in the outside artifact.

'theBiasPlanet/unoUtilities/anotherProjectTemplatesImporters/CoreUtilitiesTemplatesImporter.cpp'

@C++ Source Code
#include "theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp"

Hmm, I feel somewhat repulsed by including a 'cpp' file, but that is what I have compromised on.

Phew, the 'Visual C++''s specification of requiring the symbols to be explicitly exported entails some rather bothersome treatments . . .


References


<The previous article in this series | The table of contents of this series | The next article in this series>