We have code that uses the ICU library for working with Unicode strings. When we try to build it, we get no compile errors, but the link fails. I created a small test program with the following code:
#define U_STATIC_IMPLEMENTATION
#undef INT64_C
#undef UINT64_C
#include <unicode/coll.h>
void icu_test()
{
UErrorCode success = U_ZERO_ERROR;
Collator* myCollator = Collator::createInstance(success);
VERIFY(U_SUCCESS(success));
myCollator->setStrength(Collator::QUATERNARY);
UChar Word1[10] = _T("this");
UChar Word2[10] = _T("that");
// Compare two strings in the default locale
bool result = myCollator->greater(Word1, Word2);
}
This program also fails to link with:
error LNK2019: unresolved external symbol "public: __thiscall icu_3_2::UnicodeString::UnicodeString(wchar_t const *)" (??0UnicodeString@icu_3_2@@QAE@PB_W@Z) referenced in function "void __cdecl icu_test(void)" (?icu_test@@YAXXZ)
(Is 0UnicodeString the constructor, perhaps?) When I use dumpbin to look at the exported symbols in the ICU libraries, I see the following:
??0UnicodeString@icu_3_2@@QAE@ABV01@@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(class icu_3_2::UnicodeString const &)) ??0UnicodeString@icu_3_2@@QAE@ABV01@H@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(class icu_3_2::UnicodeString const &,int)) ??0UnicodeString@icu_3_2@@QAE@ABV01@HH@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(class icu_3_2::UnicodeString const &,int,int)) ??0UnicodeString@icu_3_2@@QAE@CPBGH@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(signed char,unsigned short const *,int)) ??0UnicodeString@icu_3_2@@QAE@G@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(unsigned short)) ??0UnicodeString@icu_3_2@@QAE@H@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(int)) ??0UnicodeString@icu_3_2@@QAE@HHH@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(int,int,int)) ??0UnicodeString@icu_3_2@@QAE@PAGHH@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(unsigned short *,int,int)) ??0UnicodeString@icu_3_2@@QAE@PBD0@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(char const *,char const *)) ??0UnicodeString@icu_3_2@@QAE@PBDH0@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(char const *,int,char const *)) ??0UnicodeString@icu_3_2@@QAE@PBDHPAUUConverter@@AAW4UErrorCode@@@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(char const *,int,struct UConverter *,enum UErrorCode &)) ??0UnicodeString@icu_3_2@@QAE@PBDHW4EInvariant@01@@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(char const *,int,enum icu_3_2::UnicodeString::EInvariant)) ??0UnicodeString@icu_3_2@@QAE@PBG@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(unsigned short const *)) ??0UnicodeString@icu_3_2@@QAE@PBGH@Z (public: __thiscall icu_3_2::UnicodeString::UnicodeString(unsigned short const *,int)) ??0UnicodeString@icu_3_2@@QAE@XZ (public: __thiscall icu_3_2::UnicodeString::UnicodeString(void))
So none of these quite match the first unresolved external, because of the stuff at the end of the identifier (e.g. @@QAE@PB_W@Z), which does not come from our code. Not sure how this is generated or what to do about it.
I tried downloading the latest version of ICU (60.2), but it only comes in a x64 version, while our program builds for x86.
I made sure that the ICU library folder is included in the link settings.
We also made sure to define U_STATIC_IMPLEMENTATION, as described in this post:
Why do I get link errors when the symbol is clearly present in the static library I link against?
In that post, they were able to locate the relevant symbol in the library, but we cannot.
Can anyone help us understand what is causing this link error?
I tried building a static library of ICU 60.2 but ran into problems and I was unable to get help from ICU support to build it.
I tried downloading pre-built ICU 59.1 libraries from this website. But had a different linker error and no idea what to do with that.
I saw that my code was trying to pass a wchar_t* to icu::greater(), which expects UnicodeString arguments, so I inferred that this is where type conversion causes the UnicodeString to be constructed. I changed the code to do an explicit conversion to UnicodeString and then pass that, but I still got the same linker error.
Finally, my coworker found some information about a potential problem with wchar_t. So that was it - our best understanding is that since our code was originally working with MS Visual C++ 6.0, and it was able to link to ICU 3.2 in that context, it was linking with the old definition of wchar_t, but the new definition of wchar_t was changing the way the name decoration was being generated. Adding -Zc:wchar_t- to the compile options (in VS 2017, go to Project Properties, C/C++ All Options, 'Additional Options' line), caused it to link successfully after a full recompile.
Very subtle.