POCO, iostreams, Windows CE and DLLs

Since release 1.4.0, the POCO C++ Libraries support Windows CE (or Windows Embedded Compact). Except for a few minor restrictions due to a lack of support for them in Windows CE, POCO works quite well on Windows CE 5.0 or later.

However, there is an interesting issue that results from the combination of having classes derived from iostream class template instantiations (std::basic_streambuf, std::basic_ios, std::basic_istream, etc.) like the stream classes provided by the Foundation library (Poco::Base64Encoder, etc.) exported by a DLL, like PocoFoundation.DLL. It turns out that the Visual C++ compiler (at least up to Visual Studio 2008, I haven't tested later versions for this) has a very interesting behavior when a class that's derived from a class template instantiation is exported by a DLL using __declspec(dllexport). In this case, the compiler will not only export the explicitly exported class, but also the base class template instantiations. For the stream classes this means that PocoFoundation.DLL will export all of the iostream class templates instantiated for char. This leads to a problem as soon as PocoFoundation.DLL is linked to a another library or executable that also uses iostreams. The compiler will also instantiate the iostream class templates in the other DLL or executable (because it does not know they can be imported from a DLL), and, comes linking time, the linker will complain about multiply defined symbols. Currently, this problem is averted by passing the /FORCE:MULTIPLE option to the linker. Therefore, the linker will ignore multiply defined symbols and just be happy with the first symbol it finds. This works for the duplicate iostream symbols, but is not a very clean solution — there may be cases where a duplicate symbol is an indication of a real problem, and this may not be noticed.

The same problem also exists in the toolchain targetting "full" Windows, but is averted in an interesting way — the Visual C++ runtime DLL (MSVCP90.DLL) exports instantiations for all iostreams templates, and the standard library headers contain template instantiations with appropriate __declspec(dllimport) declarations, thus telling the compiler to import the instantiations from the runtime DLL instead of generating them in every compilation unit.

Now, a similar mechanism can also be used on Windows CE. It's possible to add template instantiations with __declspec(dllexport/dllimport) to a POCO Foundation header, e.g. Poco/StreamUtil.h:

#if defined(_MSC_VER) && defined(_WIN32_WCE) && defined(_DLL) #include <streambuf> #include <ios> #include <istream> #include <ostream> template class Foundation_API std::basic_streambuf<char>; template class Foundation_API std::basic_ios<char>; template class Foundation_API std::basic_istream<char>; template class Foundation_API std::basic_ostream<char>; #endif

This will prevent these templates from being instantiated in other DLLs or executables, provided, however, that every translation unit using iostreams includes that header. If just a single compilation unit uses iostreams without including that header, there'll be duplicate symbols again. Another solution would be to create a separate DLL just exporting the iostreams template instantiations, with a corresponding header file. But even in this case, that header must be included in every compilation unit using iostreams, or duplicate symbols will be generated again. This may be impractical for large codebases.

For now, we'll continue to ship the POCO libraries with the /FORCE:MULTIPLE hack enabled. But the workaround outlined here may be a good alternative for some projects.

Tagged , , ,