2019-03-31

10: Dealing with Some Peculiarities of Visual C++

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

The project is built fine by GCC, but not by Visual C++? Some such cases will be dealt with here.

Topics


About: C++

The table of contents of this article


Starting Context


  • The reader has a basic knowledge on C++, even if he or she doesn't accurately understand its some widely-misrepresented elements.

Target Context


  • The reader will know how to resolve some mysterious, 'Visual C++'-specific, build errors.

Orientation


Hypothesizer 7
As I am basically a Linux user, I usually create my C++ programs first for GCC.

When I try to transplant some of my C++ programs to Windows, my first choice is, naturally, to use a GCC on Windows. . . . However, a circumstance necessitates my using Visual C++, namely, as the UNO C++ libraries for Windows are 'Visual C++'-based, I cannot link my UNO C++ programs with the UNO C++ libraries for Windows, at least easily.

I thought that if my source code conformed to the C++ standard, my programs would compile and link all right, if I used appropriate compile and link options. . . . But it was not that simple.

I had encountered some cases which required some 'Visual C++'-specific treatments, and I will deal with such cases in this article.


Main Body


1: A Mysterious "unable to match function definition to an existing declaration" Compile Error Concerning 'typedef'


Hypothesizer 7
This is what I encountered when I created a standard-map-compatible elements-order-preserved map. Let me see an excerpt of the code.

'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_coreUtilities_collections_NavigableLinkedMap_hpp__
	#define __theBiasPlanet_coreUtilities_collections_NavigableLinkedMap_hpp__
	
	using namespace ::std;
	
	namespace theBiasPlanet {
		namespace coreUtilities {
			namespace collections {
				template <typename T, typename U, typename W = less <T>> class NavigableLinkedMap {
					public:
						typedef T key_type;
						typedef U mapped_type;
						typedef W key_compare;
					public:
						typedef pair <key_type const, mapped_type> value_type;
						class BaseIterator {
						};
						class NonConstantIterator : public BaseIterator {
							public:
								typedef pair <key_type const, mapped_type> value_type;
								virtual value_type & operator * ();
						};
						class ConstantIterator : public BaseIterator {
							public:
								typedef pair <key_type const, mapped_type> const value_type;
								virtual value_type & operator * ();
						};
				};
			}
		}
	}
#endif

'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.cpp'

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

namespace theBiasPlanet {
	namespace coreUtilities {
		namespace collections {
			template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::NonConstantIterator::value_type & NavigableLinkedMap <T, U, W>::NonConstantIterator::operator * () {
				// Omitted
			}
			
			template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type & NavigableLinkedMap <T, U, W>::ConstantIterator::operator * () {
				// Omitted
			}
		}
	}
}

While that code compiles fine for GCC, Visual C++ complaints about "template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type & NavigableLinkedMap <T, U, W>::ConstantIterator::operator * ()" that it is "unable to match function definition to an existing declaration" . . .

Note that the almost the same method, "template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::NonConstantIterator::value_type & NavigableLinkedMap <T, U, W>::NonConstantIterator::operator * ()", is accepted all right. . . . Why? . . .

It has turned out that the return type, "typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type &" in the source file is the cause. But that is not particularly different from the accepted method . . .

It has turned out that the last "const" in "typedef pair <key_type const, mapped_type> const value_type" in the header file is the cause. . . . Why? Honestly, I do not know. If the "const" was eliminated and the return type in the 'cpp' file is changed to "typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type const &", the code would compile fine. Hmm . . .

However, as I cannot change the definition of 'value_type', which is a published type, instead, I have added a type, 'NonConstantvalue_type' as 'typedef pair <key_type const, mapped_type>', and changed the expression of the return type in the 'cpp' file to 'typename NavigableLinkedMap <T, U, W>::ConstantIterator::NonConstantvalue_type const &', which does not really change the return type. . . . Does that work fine? Yes.


2: Any Symbol in any DLL Has to Be Exported in Order to Be Visible from the Outside of the DLL, and What That Requirement Entails, Especially for Templates


Hypothesizer 7
First, although this seems to be rather a common piece of knowledge for Visual C++ programmers, I will state here because I did not know and spent some time in solving the problem.

Any symbol (for example, a class name) in any DLL is not visible from the outside of the DLL just because it resides in the DLL; it has to be explicitly exported from the DLL.

I do not understand why Visual C++ has to require such an extra task which GCC does not, from any programmer, but I have to face the reality.

Although the symbol 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 a source file because creating the "module definition file" seems more tedious.

What to be done for exporting any symbol is to put a qualification, '__declspec (dllexport)', at a certain place.

"at a certain place"? . . . For example, this exports the symbols of a class and its members.

'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

However, as the header file can be included also in any outside artifact, the appearance of the qualification has to be controlled so that the qualification appears only when the header file is included in the DLL, and another qualification, '__declspec (dllimport)', should (although not has to be) appear when the header file is included in any outside artifact.

So, how about this, specifying the 'DLL' definition at the compile time only when the compiled artifact is a DLL?

'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

. . . No, that is not good, for an outside artifact that uses the DLL may be another DLL . . .. So, whether the built artifact is a DLL or not is not the issue, but whether the symbol is owned by the built artifact 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? . . . Yes, if I export and import no template instance . . .

"template instance"? . . . I have to understand that any template is not any class or any function and cannot be exported or imported; any template instance is a class or a function, which is what can be exported and imported.

As I have discussed in a section of a previous article, I have two options on where the template is instantiated, and if I opt to let the users instantiate the template in their programs, there is no need for the DLL to export any template instance, but if I opt to make the DLL instantiate the template (which is my primary choice), the template instances have to be exported from the DLL.

Let me suppose that I have chosen the latter option, through this section. What should I do?

In fact, in the DLL, I have to specify '__declspec (dllexport)' in the explicit template instantiations, like these.

'theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp'

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

#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllexport)

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 'template <typename T, typename U, typename W = less <T>> class NavigableLinkedMap' is a standard-map-compatible elements-order-preserved map template introduced in a previous article, and 'template <typename ... V> pair <iterator, bool> emplace (V && ... a_arguments)' is one of its method templates.

So, what should I do in order to make any outside artifact to import the symbols?

In fact, the outside artifact should have this code.

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

#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllimport)

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

#ifdef __theBiasPlanet_coreUtilities__
	#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllexport)
#else
	#define __theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__ __declspec (dllimport)
#endif

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.

Finally, as I do not need those qualifications for GCC, I control the definition of '__theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__' 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

In fact, I will not scatter that code into source files, but put it into a header file and make source files include the header file.

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


3: A Type of Expression for Explicitly Instantiating Any Class Constructor Template Is Not Allowed


Hypothesizer 7
This explicit instantiation of a class constructor template (which compiles fine for GCC) causes a series of enigmatic compile errors for Visual C++, where the signature of the template is 'template <typename V> NavigableLinkedMap (V a_iteratorPointedAtFirstElement, V a_iteratorPointedAtLastElementNotIncluded, key_compare const & a_keysComparer = key_compare ())'.

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

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

template NavigableLinkedMap <string, int>::NavigableLinkedMap <NavigableLinkedMap <string, int>::iterator> (NavigableLinkedMap <string, int>::iterator a_iteratorPointedAtFirstElement, NavigableLinkedMap <string, int>::iterator a_iteratorPointedAtLastElementNotIncluded, NavigableLinkedMap <string, int>::key_compare const & a_keysComparer);

The errors are these: "error C2976: 'theBiasPlanet::coreUtilities::collections::NavigableLinkedMap': too few template arguments", "error C2146: syntax error: missing ')' before identifier 'a_iteratorPointedAtFirstElement'", "error C2146: syntax error: missing ';' before identifier 'a_iteratorPointedAtFirstElement'", "error C4430: missing type specifier - int assumed. Note: C++ does not support default-int", and "error C2226: syntax error: unexpected type 'theBiasPlanet::coreUtilities::collections::NavigableLinkedMap<std::string,int,std::less<T>>::iterator'".

Huh? I do not understand them a bit . . .

As the template is a little complex, I tried a simpler constructor template like this.

'theBiasPlanet/tests/templatesTest1/ClassA.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_tests_templatesTest1_ClassA_hpp__
	#define __theBiasPlanet_tests_templatesTest1_ClassA_hpp__
	
	namespace theBiasPlanet {
		namespace tests {
			namespace templatesTest1 {
				template <typename T> class ClassA {
					public:
					    ClassA ();
					    template <typename U> ClassA (U a_argument0);
				};
			}
		}
	} 
#endif

'theBiasPlanet/tests/templatesTest1/TemplatesInstantiator.cpp'

@C++ Source Code
#include "theBiasPlanet/tests/templatesTest1/ClassA.tpp"

using namespace ::theBiasPlanet::tests::templatesTest1;

template class ClassA <int>;
template ClassA <int>::ClassA <double> (double a_argument0); // This causes a series of errors for Visual C++

The last line causes these errors for Visual C++ (while it is fine for GCC), "error C2039: 'theBiasPlanet::tests::templatesTest1::ClassA<double>': is not a member of 'theBiasPlanet::tests::templatesTest1::ClassA<int>'" and "error C2062: type 'double' unexpected", which are very different from the above errors, but are as enigmatic as them.

Actually, removing the template type specification, "<NavigableLinkedMap <string, int>::iterator>" or "<double>", after the method name, 'NavigableLinkedMap <string, int>::NavigableLinkedMap' or 'ClassA <int>::ClassA' (I think that they should be regarded as the method names), like theses, solves the problem.

@C++ Source Code
template NavigableLinkedMap <string, int>::NavigableLinkedMap (NavigableLinkedMap <string, int>::iterator a_iteratorPointedAtFirstElement, NavigableLinkedMap <string, int>::iterator a_iteratorPointedAtLastElementNotIncluded, NavigableLinkedMap <string, int>::key_compare const & a_keysComparer);

@C++ Source Code
template ClassA <int>::ClassA (double a_argument0);

Well, according to my understanding that any constructor is a function, and such an exceptional treatment for constructors does not make sense. I mean, I guess that the exceptional treatment is related with any constructor template's not being able to be implicitly instantiated with the template types explicitly specified; let me see an example.

'theBiasPlanet/tests/templatesTest1/ClassB.hpp'

@C++ Source Code
#ifndef __theBiasPlanet_tests_templatesTest1_ClassB_hpp_
	#define __theBiasPlanet_tests_templatesTest1_ClassB_hpp__
	
	namespace theBiasPlanet {
		namespace tests {
			namespace templatesTest1 {
				template <typename T> class ClassB {
					public:
					    ClassB ();
					    template <typename U> ClassB ();
				};
			}
		}
	} 
#endif

In fact, the C++ standard does not allow the type, 'U', to be specified, for example, like this in an implicit instantiation of the constructor.

@C++ Source Code
	ClassB <int> <double> l_classB (); // This is not allowed, where 'double' is meant to be a specification of 'U'.

The reason of the prohibition seems to be, that '<double>' has to be placed after the function name, but no function name appears in that statement (in fact, the C++ standard even declares that any constructor does not have any function name) . . .

I disagree: the C++ standard can just enact a rule that allows explicitly specifying the constructor template types: such a rule as the function template types have to appear after the function name is just an arbitrary rule that the C++ standard itself has enacted and is free to renounce any time. And why does C++ standard have to take an arbitrary interpretation that any constructor does not have any function name: the function name can be very reasonably interpreted to equal the class name?


4: A Bug That Concerns a Standard-Template-Library String Converter


Hypothesizer 7
This code causes a link error, "error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class std::locale::id std::codecvt<char16_t,char,struct _Mbstatet>::id"", for Visual C++ 2017 while it is good for GCC.

@C++ Source Code
#include <codecvt>
#include <locale>

				wstring_convert <codecvt_utf8_utf16 <char16_t>, char16_t> l_wstringConverter;

It has turned out that it is an old bug of Visual C++, which exists since Visual C++ 2015 . . .. I say that it is too old; I understand that some bugs could sneak into a product, but any bug will have to be fixed as soon as possible . . .. I do not agree that the reason of preserving the binary compatibility cited in the page justifies not fixing the bug. While preserving the binary compatibility is ideal by itself, is breaking the compatibility with the C++ standard template library fine for them? . . . If there is a bug that entails a security vulnerability, will they leave the bug as it is, in order to preserve the binary compatibility with a buggy old version? . . .

Anyway, they seem to suggest to just use 'wchar_t' instead of 'char16_t' as a remedy.

Does that solve the problem? . . . I want a function that converts any UTF8 datum to a 'u16string' datum, like this.

@C++ Source Code
#include <codecvt>
#include <locale>

			u16string getUtf16String (string const & a_utf8String) {
				wstring_convert <codecvt_utf8_utf16 <char16_t>, char16_t> l_wstringConverter;
				return l_wstringConverter.from_bytes (a_utf8String.data ());
			}

While changing 'char16_t' to 'wchar_t' changes the return type of 'from_bytes' to 'wstring', I do not want any 'wstring' instance, but a 'u16string' instance . . .. Note that any 'wstring' instance cannot just be cast to the 'u16string' type.

After all, I have to create a 'u16string' instance using the 'wstring' instance, like this.

@C++ Source Code
				wstring l_wstring = l_wstringConverter.from_bytes (a_utf8String.data ());
				return u16string (l_wstring.begin (),  l_wstring.end ());

As that code does not work for Linux (in which, 'wchar_t' is different in length from 'char16_t'), I have to have a code like this (supposing that building in Linux equals using GCC).

@C++ Source Code
			u16string getUtf16String (string const & a_utf8String) {
#ifdef GCC
				wstring_convert <codecvt_utf8_utf16 <char16_t>, char16_t> l_wstringConverter;
				return l_wstringConverter.from_bytes (a_utf8String.data ());
#else
				wstring_convert <codecvt_utf8_utf16 <wchar_t>, wchar_t> l_wstringConverter;
				wstring l_wstring = l_wstringConverter.from_bytes (a_utf8String.data ());
				return u16string (l_wstring.begin (),  l_wstring.end ());
#endif
			}


5: Any Stack Array Has to Be Defined with a Compile-Time-Determined Length


Hypothesizer 7
In fact, this is not any peculiarity of Visual C++, but is what conforms to the C++ standard.

Anyway, any stack array (array that is stored in the stack) has to be defined with a compile-time-determined length for Visual C++, while it is not so for GCC.

I understand that Visual C++ just conforms to the C++ standard, but it is rather inconvenient.

However, there is a remedy: I can create the array in the heap with the run-time-determined length and manually destroy it at an appropriate time.


6: The Conclusion and Beyond


Hypothesizer 7
Those are what I have encountered when I tried to transplant some of my C++ programs from GCC to Visual C++.

Being troubled by such differences, I cannot help but feel the Java's benefit of the independence of operating systems. Although differences in the binary formats are fine, cannot C++ source files be perfectly (at least, almost) portable as far as they conform to the C++ standard?

Anyway, as I encounter other peculiarities, I will report them in future articles.


References


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