スポンサーリンク
icuライブラリのラッパーを作成したくなり、hppにicu::UnicodeStringのポインタを持たせ、cpp内でnewする構造にした。この時、前方宣言したらエラーC2757が発生。原因はicuという名前空間が実は存在していないかららしい。
まず、エラーの出るコードは以下。
#pragma once // クラスが名前空間に入っている時は前方宣言をこうする namespace icu{ // C2757 class UnicodeString; } // icu::UnicodeStringのラッパー struct MyICU { icu::UnicodeString* ustr; }; // ICUライブラリのUnicodeStringのラッパーを作成 MyICU make_my_icu(const char16_t* text);
#include <unicode/ucnv.h> #include <unicode/brkiter.h> #include"icuwrap.h" // 要リンク #pragma comment(lib, "icuuc.lib") #pragma comment(lib, "icudt.lib") MyICU make_my_icu(const char16_t* text) { MyICU tmp; tmp.ustr = new icu::UnicodeString(text); return tmp; }
#include "icuwrap.h" int main() { MyICU micu = make_my_icu(u"hello"); }
すると以下のコンパイルエラーが発生する
icuというリテラルは unicode/uversion.h の中で定義されているのだが、実はこれはU_ICU_NAMESPACEマクロで生成されたもののエイリアスで、さらにこれはU_ICU_ENTRY_POINT_RENAMEマクロで作成されている
unicode/uversion.h Line 105~
# define U_ICU_NAMESPACE U_ICU_ENTRY_POINT_RENAME(icu) namespace U_ICU_NAMESPACE { } namespace icu = U_ICU_NAMESPACE;
ではこの U_ICU_ENTRY_POINT_RENAME マクロの定義はというと、こちらは unicode/uvernum.h で定義されている。
unicode/uversion.h Line 128~
# define U_DEF_ICU_ENTRY_POINT_RENAME(x,y) x ## y # define U_DEF2_ICU_ENTRY_POINT_RENAME(x,y) U_DEF_ICU_ENTRY_POINT_RENAME(x,y) # define U_ICU_ENTRY_POINT_RENAME(x) U_DEF2_ICU_ENTRY_POINT_RENAME(x,U_ICU_VERSION_SUFFIX)
ここで ## はトークン結合演算子で、x は 「icu」、U_ICU_VERSION_SUFFIXは「_69」など、_ + ライブラリのバージョンが入っている
unicode/uversion.h Line 89
#define U_ICU_VERSION_SUFFIX _69
つまり「icu」は、「icu_69」のエイリアスである。
前方宣言で自分が定義した「namespace icu」というモノが既にあるのに、同じ「icu」をicu_69のエイリアスとして使おうとしたことになって、エラーが起こっている。
バージョンが固定でいいなら以下のように正規の名前空間で前方宣言すれば解決する
// クラスが名前空間に入っている時は前方宣言をこうする namespace icu_69{ class UnicodeString; } struct MyICU { icu_69::UnicodeString* ustr; }; // ICUライブラリのUnicodeStringのラッパーを作成 MyICU make_my_icu(const char16_t* text);
しかしどうせ隠すなら素直にpimplとして完全に隠す方がいい。
U_DISABLE_RENAMINGマクロを使えば解決しそうに見えるが、マニュアルには「内部処理用だから使うな」と書いてあるのであきらめる。