2020年8月2日日曜日

10: Visual C++の一部の奇癖に対処する方法

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

C++プロジェクトが、GCCでは問題なくビルドされるのに、Visual C++ではされない?そういういくつかのケースがここで対処されます。

話題


About: C++

この記事の目次


開始コンテキスト


  • 読者は、C++の基本的知識を持っている、その広く不正確に伝えられている一部の要素たちを正確に理解していないとしても。

ターゲットコンテキスト



  • 読者は、Visual C++に特有な、一部の謎めいたビルドエラーを解決する方法を知る。

オリエンテーション


Hypothesizer 7
私は、基本的にLinuxユーザーなので、通常、自分のC++プログラムを、まずは、GCCように作成する。

自分のC++プログラムの一部をWindowsに移植しようとする時、私の最初の選択は、自然のことに、Windows上のGCCを使用することだ。...しかしながら、ある状況が、Visual C++を使用することを余儀なくさせている、具体的には、Windows用のUNO C++ライブラリ群がVisual C++ベースなので、自分のUNO C++プログラムをWindows用のUNO C++ライブラリ群とリンクできない、少なくとも、簡単には。

自分のソースコードがC++標準に則っていれば、自分のプログラムは問題なくコンパイルおよびリンクするだろう、と考えていた、適切な、コンパイルおよびリンクのオプション群を使用すれば。...しかし、事はそれほど単純でなかった。

私は、Visual C++特有の取扱いが求められる一部のケースに遭遇し、そうしたケース群に本記事で対処しよう。


本体


1: 'typedef'に関係した、謎めいた「unable to match function definition to an existing declaration」コンパイルエラー


Hypothesizer 7
以下が、スタンダードmap互換の要素群順序保持マップを作成した際に私が遭遇したものだ。コードの抜粋を見てみよう。

'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp'

@C++ ソースコード
#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++ ソースコード
#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
			}
		}
	}
}

そのコードは、GCCでは問題なくコンパイルするのだが、Visual C++は、「template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type & NavigableLinkedMap <T, U, W>::ConstantIterator::operator * ()」について、「unable to match function definition to an existing declaration」だと不平を言う...

注目すべきことに、ほとんど同じメソッドである「template <typename T, typename U, typename W> typename NavigableLinkedMap <T, U, W>::NonConstantIterator::value_type & NavigableLinkedMap <T, U, W>::NonConstantIterator::operator * ()」 は問題なく受け入れられている。...なぜ?...

判明したのだが、ソースファイル内の、リターンタイプである「typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type &」が原因なのだ。しかし、それは、受け入れられたメソッドのものと特に違わないのだが...

判明したのだが、ヘッダーファイル中の「typedef pair <key_type const, mapped_type> const value_type」中の最後の「const」が原因なのだ。...なぜか?正直、私は知らない。もしも、「const」が取り除かれて、'cpp'ファイル内のリターンタイプが「typename NavigableLinkedMap <T, U, W>::ConstantIterator::value_type const &」に変更されたら、コードは問題なくコンパイルする。ふーむ...

しかしながら、公開されるタイプである'value_type'の定義を私は変更することはできないので、代わりに、'NonConstantvalue_type'というタイプを、'typedef pair <key_type const, mapped_type>'として追加し、'cpp'ファイル中のリターンタイプの表現を、'typename NavigableLinkedMap <T, U, W>::ConstantIterator::NonConstantvalue_type const &'に変更した、それは、実際には、リターンタイプを変えていないのであるが。...それでうまくいく?はい。


2: 任意のDLL内の任意のシンボルは、エクスポートされなければならない、そのDLL内の外部から可視であるようにするためには、そして、その要件がもたらすこと、特に、テンプレートに対して


Hypothesizer 7
まず、これは、Visual C++プログラマーたちにとってはむしろ常識であるようだが、ここで述べておく、なぜなら、私は、それを知らず、問題を解決するのにそれなりの時間を費やしたから。

任意のDLL内の任意のシンボル(例えば、クラス名)は、ただそれがそのDLL内に存在しているというだけの理由で、そのDLLの外部から可視にはならない、それは、そのDLLから明示的にエクスポートされなければならない。

なぜVisual C++がプログラマーにそのような、GCCが要求しない余分のタスクを要求しなければならないのか、私は理解しないが、その現実を直視しなければならない。

シンボルは、「モジュール定義ファイル」と呼ばれるものを作成して、それをリンカーに指定することで、なんらのソースファイル(ヘッダーファイルまたは'cpp'ファイル)にも手を加えることなしにエクスポートできるが、ソースファイルに手を加える措置を私は取ろう、なぜなら、「モジュール定義ファイル」のほうがより面倒のようだから。

任意のシンボルをエクスポートするためにするべきことは、'__declspec (dllexport)'という修飾をつけることだ、特定の位置に。

「特定の位置」?...例えば、以下は、あるクラスおよびそのメンバー群のシンボル群をエクスポートする。

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ ソースコード
#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

しかしながら、そのヘッダーファイルは、任意の外部作成物からもインクルードされうるので、その修飾の出現は、そのヘッダーファイルがそのDLL内でインクルードされる場合にのみ出現し、そのヘッダーファイルが外部作成物でインクルードされる場合には、'__declspec (dllimport)'という別の修飾が現われる(それは必須ではないが)ようにコントロールされなければならない。

それでは、これではどうだろうか、コンパイルされる作成物がDLLである場合のみ、’DLL'という定義をコンパイル時に指定するというのでは?

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ ソースコード
#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

...いや、それではだめだ、だって、当該DLLを使用する外部作成物が別のDLLかもしれないから。...したがって、ビルドされる作成物がDLLか否かが問題なのではなく、当該シンボルが、ビルドされる作成物によって所有されているか否かが問題なのだ。

したがって、定義名は、作成物固有でなければならない、以下のように('__theBiasPlanet_coreUtilities__'という定義はコンパイル時に指定される)。

'theBiasPlanet/coreUtilities/constantsGroups/FileNameSuffixesConstantsGroup.hpp'

@C++ ソースコード
#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

それで全て?...そう、もしも、テンプレートインスタンスをエクスポート・インポートしないのであれば。

「テンプレートインスタンス」?...理解しなければならないのだが、テンプレートは、クラスやファンクションではなく、エクスポートもインポートもできない、テンプレートインスタンスがクラスまたはファンクションなのであって、それがエクスポート・インポートできるものなのである。

ある以前の記事のあるセクションで議論したように、どこでテンプレートをインスタンス化するかについて、私には2つのオプションがあり、テンプレートをユーザに彼らのプログラム内でインスタンス化させることを選択するのであれば、DLLがテンプレートインスタンスをエクスポートする必要はない、しかし、テンプレートをDLLにインスタンス化させることを選択するのであれば(それが私の第1選択である)、テンプレートインスタンス群はDLLからエクスポートされなければならない。

本セクションを通して、後者のオプションを選択した、と想定しよう。私はどうすべきだろうか?

実のところ、DLL内で、明示的なテンプレートインスタンス化において、'__declspec (dllexport)'を指定しなければならない、以下のように。

'theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp'

@C++ ソースコード
#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);

ここで、'template <typename T, typename U, typename W = less <T>> class NavigableLinkedMap'は、ある以前の記事で紹介されたスタンダードmap互換の要素順保持マップテンプレートであり、'template <typename ... V> pair <iterator, bool> emplace (V && ... a_arguments)'は、そのメソッドテンプレート群の内の1つである。

それでは、外部作成物にそれらのシンボルをインポートさせるには、私はどうすべきなのか?

実のところ、外部作成物は、以下のコードを持つべきだ。

@C++ ソースコード
#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);

注目すべきは、'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.tpp'はインクルードできず、'theBiasPlanet/coreUtilities/collections/NavigableLinkedMap.hpp'をインクルードするということだ: それが、'__declspec (dllimport)'修飾を使用するためのルールであり、そもそも、DLLからテンプレートインスタンス群をエクスポートすることの要諦は、'tpp'ファイルが、DLLのユーザには暴露されないことである。

外部作成物内にて上記コードを直接に保持する'cpp'ファイルを新たに作成することもできるが、私は、DLL内にソースファイルを以下のように作成し、それを外部作成物にインクルードさせる。

'theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp'

@C++ ソースコード
#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);

そして、外部作成物に、以下のようなソースファイルを作成する。

'theBiasPlanet/unoUtilities/anotherProjectTemplatesImporters/CoreUtilitiesTemplatesImporter.cpp'

@C++ ソースコード
#include "theBiasPlanet/coreUtilities/templatesInstantiator/TemplatesInstantiator.cpp"

ふーむ、'cpp'ファイルをインクルードするというのには、若干、嫌悪感を感じるが、それが私の妥協点だ。

最後に、それらの修飾はGCCには必要ないので、'__theBiasPlanet_coreUtilities_symbolExportingOrImportingForVisualCplusplus__'の定義を以下のようにコントロールする、コンパイル時に'GCC'定義を指定するかしないかして。

@C++ ソースコード
#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

実際には、そのコードをソースファイル群に散らばらせるようなことはせず、1つのヘッダーファイルに入れて、ソースファイル群にはそのヘッダーファイルをインクルードさせる。

いずれにせよ、Visual C++の、シンボルが明示的にエクスポートされなければならないという仕様が、かなり面倒な処置を必要にしてしまっている。


3: クラスコンストラクタテンプレートを明示的にインスタンス化するためのあるタイプの表現が許されない


Hypothesizer 7
あるクラスコンストラクタテンプレートの以下の明示的インスタンス化(GCCでは問題なくコンパイルする)は、Visual C++では、一連の謎めいたコンパイルエラーを引き起こす(テンプレートのシグネチャーは、'template <typename V> NavigableLinkedMap (V a_iteratorPointedAtFirstElement, V a_iteratorPointedAtLastElementNotIncluded, key_compare const & a_keysComparer = key_compare ())'である)。

@C++ ソースコード
#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);

エラー群は以下のとおり: 「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'」。

はあ?私には少しも理解できない...

上記テンプレートは少し複雑なので、以下のような、もっと単純なコンストラクタテンプレートで試してみた。

'theBiasPlanet/tests/templatesTest1/ClassA.hpp'

@C++ ソースコード
#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++ ソースコード
#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++

上記最終行が、Visual C++では、以下のエラー群を引き起こす(GCCでは問題ない)。「error C2039: 'theBiasPlanet::tests::templatesTest1::ClassA<double>': is not a member of 'theBiasPlanet::tests::templatesTest1::ClassA<int>'" and "error C2062: type 'double' unexpected」、それは、先程のエラー群とは大きく違うが、先程と同じぐらい謎めいている。

実は、メソッド名、'NavigableLinkedMap <string, int>::NavigableLinkedMap'または'ClassA <int>::ClassA'(私は、それらはメソッド名であると考えるべきだと思う)の後の、テンプレートタイプ指定である、「<NavigableLinkedMap <string, int>::iterator>」または「<double>」を取り除けば(以下のように)、問題は解決する。

@C++ ソースコード
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++ ソースコード
template ClassA <int>::ClassA (double a_argument0);

えーと、私の理解では、コンストラクタはファンクションであって、コンストラクタに対するそのような例外的扱いは私には合点がいかない。つまり、その例外的扱いは、コンストラクタテンプレートは、テンプレートタイプを明示的に指定して暗黙的にインスタンス化できないということに関係している、と私は推測する。1つの例を見てみよう。

'theBiasPlanet/tests/templatesTest1/ClassB.hpp'

@C++ ソースコード
#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

実のところ、C++標準は、タイプ’U'を指定することを許さない、例えば、以下のように、コンストラクタの暗黙インスタンス化において。

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

その禁止の理由は、'<double>'はファンクション名の後に置かれなければならないが、何らのファンクション名もそのステートメントに現われないことでことであるようだ(実際、C++標準は、コンストラクタはファンクション名を持たないと断言さえしている)...

私は同意しない: C++標準は、コンストラクタテンプレートタイプの指定を許すルールを制定すればよいだけだ: ファンクションテンプレートタイプはファンクション名の後に現われなければならないというようなルールは、C++標準自身が制定したただの恣意的なルールにすぎないのであって、いつでも自由に破棄することができる。それに、なぜ、C++標準は、コンストラクタはファンクション名を持たないという恣意的な解釈をとらなければならないのだろうか: ファンクション名はとてもリーズナブルに、クラス名に等しいと解釈できるのに?


4: スタンダードテンプレートライブラリの文字列コンバータに関係するバグ


Hypothesizer 7
以下のコードは、リンクエラー(「error LNK2001: unresolved external symbol "__declspec(dllimport) public: static class std::locale::id std::codecvt<char16_t,char,struct _Mbstatet>::id"」)を引き起こす、Visual C++ 2017において、GCCでは問題ないのであるが。

@C++ ソースコード
#include <codecvt>
#include <locale>

				wstring_convert <codecvt_utf8_utf16 <char16_t>, char16_t> l_wstringConverter;

判明したことには、それは、Visual C++ 2015以来存在している、Visual C++の古くからあるバグである。...私に言わせれば、それは古すぎる。プロダクトにいくらかのバグが忍び込んでしまうことは理解するが、バグはできるだけ早く修正されるべきだろう。...そのページに挙げられている、バイナリ互換性を維持するためという理由がバグを修正しないことを正当化するとは、私は同意しない。バイナリ互換性を維持することはそれ自体としては理想的ではあるが、C++スタンダードテンプレートライブラリとの互換性を破ることは、彼らにとってオーケーなのだろうか?...仮に、セキュリティ上の脆弱性を伴うバグがあった場合、彼らは バグをそのまま放置するのであろうか、バグのある古いバージョンとのバイナリ互換性を維持するために?...

とにかく、彼らは、対策として、'char16_t'の代わりに'wchar_t'を使えばいいだろうと、示唆しているようだ。

それで問題が解決する?...私は、UTF8データを'u16string'データに変換するファンクションが欲しいのですよ、以下のようにして。

@C++ ソースコード
#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 ());
			}

'char16_t'を'wchar_t'に変更すると、'from_bytes'のリターンタイプが'wstring'に変更されるが、私は、'wstring'インスタンスではなく、'u16string'インスタンスが欲しいのですよ。...注意すべきは、'wstring'インスタンスは、'u16string'型にただキャストするというわけにはいかないということだ。

結局、私は、'u16string'インスタンスを、'wstring'インスタンスを使用して作成しなければならない、以下のように。

@C++ ソースコード
				wstring l_wstring = l_wstringConverter.from_bytes (a_utf8String.data ());
				return u16string (l_wstring.begin (),  l_wstring.end ());

そのコードは、Linuxではうまくいかないので(そこでは、'wchar_t'は'char16_t'とは長さが違う)、以下のようなコードにしないといけない(LinuxでビルドすることがGCCを使うこととイコールであると仮定して)。

@C++ ソースコード
			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: スタック内配列はコンパイル時に決定される長さで定義されなければならない


Hypothesizer 7
実際には、これはVisual C++の奇癖ではなく、C++標準に則っているものだ。

いずれにせよ、スタック内配列(スタック内に格納される配列)は、Visual C++では、コンパイル時に決定される長さで定義されなければならない、GCCではそうではないが。

Visual C++はC++標準に則っているだけだ、ということは理解するが、それはかなり不便である。

しかし、対策はある: 配列をヒープに、実行時に決定される長さで作成し、適切なタイミングでそれを手動で廃棄することはできる。


6: 結びとその先


Hypothesizer 7
それらが、私が、自分のC++プログラム群のいくつかをGCCからVisual C++へ移植しようとした時に遭遇したものだ。

そのような違いに煩わされて、Javaの、オペレーティングシステム独立であることの恩恵を感じざるを得ない。バイナリーフォーマットにおける差異はいいのであるが、C++ソースファイルは、C++標準に則っている限り完全に(少なくとも、ほとんど)ポータブルであるというようにできないのであろうか?

とにかく、他の奇癖に遭遇したら、以降の記事群にて報告しよう。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>