std::basic_stringstream<unsigned char> won't compile with MSVC 10
- by Michael J
I'm trying to get UTF-8 chars to co-exist with ANSI 8-bit chars. My strategy has been to represent utf-8 chars as unsigned char so that appropriate overloads of functions can be used for the two character types.
e.g.
namespace MyStuff
{
typedef uchar utf8_t;
typedef std::basic_string<utf8_t> U8string;
}
void SomeFunc(std::string &s);
void SomeFunc(std::wstring &s);
void SomeFunc(MyStuff::U8string &s);
This all works pretty well until I try to use a stringstream.
std::basic_ostringstream<MyStuff::utf8_t> ostr;
ostr << 1;
MSVC Visual C++ Express V10 won't compile this:
c:\program files\microsoft visual studio 10.0\vc\include\xlocmon(213):
warning C4273: 'id' : inconsistent dll linkage
c:\program files\microsoft visual studio 10.0\vc\include\xlocnum(65) :
see previous definition of
'public: static std::locale::id std::numpunct<unsigned char>::id'
c:\program files\microsoft visual studio 10.0\vc\include\xlocnum(65) :
while compiling class template static data member 'std::locale::id
std::numpunct<_Elem>::id'
with
[
_Elem=Tk::utf8_t
]
c:\program files\microsoft visual studio 10.0\vc\include\xlocnum(1149) :
see reference to function template instantiation
'const _Facet &std::use_facet<std::numpunct<_Elem>>(const std::locale &)'
being compiled
with
[
_Facet=std::numpunct<Tk::utf8_t>,
_Elem=Tk::utf8_t
]
c:\program files\microsoft visual studio 10.0\vc\include\xlocnum(1143) :
while compiling class template member function
'std::ostreambuf_iterator<_Elem,_Traits>
std::num_put<_Elem,_OutIt>::
do_put(_OutIt,std::ios_base &,_Elem,std::_Bool) const'
with
[
_Elem=Tk::utf8_t,
_Traits=std::char_traits<Tk::utf8_t>,
_OutIt=std::ostreambuf_iterator<Tk::utf8_t,std::char_traits<Tk::utf8_t>>
]
c:\program files\microsoft visual studio 10.0\vc\include\ostream(295) :
see reference to class template instantiation 'std::num_put<_Elem,_OutIt>'
being compiled
with
[
_Elem=Tk::utf8_t,
_OutIt=std::ostreambuf_iterator<Tk::utf8_t,std::char_traits<Tk::utf8_t>>
]
c:\program files\microsoft visual studio 10.0\vc\include\ostream(281) :
while compiling class template member function
'std::basic_ostream<_Elem,_Traits> &
std::basic_ostream<_Elem,_Traits>::operator <<(int)'
with
[
_Elem=Tk::utf8_t,
_Traits=std::char_traits<Tk::utf8_t>
]
c:\program files\microsoft visual studio 10.0\vc\include\sstream(526) :
see reference to class template instantiation
'std::basic_ostream<_Elem,_Traits>' being compiled
with
[
_Elem=Tk::utf8_t,
_Traits=std::char_traits<Tk::utf8_t>
]
c:\users\michael\dvl\tmp\console\console.cpp(23) :
see reference to class template instantiation
'std::basic_ostringstream<_Elem,_Traits,_Alloc>' being compiled
with
[
_Elem=Tk::utf8_t,
_Traits=std::char_traits<Tk::utf8_t>,
_Alloc=std::allocator<uchar>
]
.
c:\program files\microsoft visual studio 10.0\vc\include\xlocmon(213):
error C2491: 'std::numpunct<_Elem>::id' : definition of dllimport
static data member not allowed
with
[
_Elem=Tk::utf8_t
]
Any ideas?
** Edited 19 June 2012 **
OK, I've gotten closer to understanding this, but not how to solve it.
As we all know, static class variables get defined twice:
once in the class definition and
once outside the class definition which establishes storage space.
e.g.
// in .h file
class CFoo
{
// ...
static int x;
};
// in .cpp file
int CFoo::x = 42;
Now in the VC10 headers we get something like this:
template<class _Elem>
class numpunct : public locale::facet
{
// ...
_CRTIMP2_PURE static locale::id id;
// ...
}
When the header is included in an application, _CRTIMP2_PURE is defined as __declspec(dllimport), which means that the variable is imported from a dll.
Now the header also contains the following
template<class _Elem>
locale::id numpunct<_Elem>::id;
Note the absence of the __declspec(dllimport) qualifier.
i.e. The class declaration says that the static linkage of the id variable is in the dll, but for the general case, it gets declared outside the dll.
For the known cases, there are specialisations.
template locale::id numpunct<char>::id;
template locale::id numpunct<wchar_t>::id;
These are protected by #ifs so that they are only included when building the DLL. They are excluded otherwise.
i.e. the char and wchar_t versions of numpunct ARE inside the dll
So we have the class definition saying that id's storage is in the DLL, but that is only true for the char and wchar_t specialisations, meaning that my unsigned char version is doomed. :-(
The only way forward that I can think of is to create my own specialisation: basically copying it from the header file and fixing it. This raises many issues.
Anybody have a better idea?