STL: When wchar_t is a real type, users shouldn't see unsigned short machinery (original) (raw)

Because the STL grudgingly supports /Zc:wchar_t- (see #212), we occasionally need to detect _NATIVE_WCHAR_T_DEFINED.

For a correct (although weird) example, the STL always defines numeric_limits<wchar_t>:

template <>
class numeric_limits<wchar_t> : public _Num_int_base {

Then if wchar_t is a real type, it additionally defines numeric_limits<unsigned short>:

#ifdef _NATIVE_WCHAR_T_DEFINED
// CLASS numeric_limits
template <>
class numeric_limits : public _Num_int_base {

This is backwards, but it works. Users with real wchar_t get both specializations, while users with fake wchar_t get only one.

However, the STL appears to have incorrect logic elsewhere. It follows the numeric_limits pattern for char_traits:

// STRUCT char_traits<wchar_t>
template <>
struct char_traits<wchar_t> : _WChar_traits<wchar_t> {}; // properties of a string or stream wchar_t element
#ifdef _NATIVE_WCHAR_T_DEFINED
// STRUCT char_traits
template <>
struct char_traits : _WChar_traits {
// properties of a string or stream unsigned short element
};
#endif // _NATIVE_WCHAR_T_DEFINED

But when wchar_t is a real type, char_traits<unsigned short> doesn't make any sense!

<fstream> has many similar examples. basic_ifstream is always constructible from const wchar_t*:

explicit basic_ifstream(
const wchar_t* _Filename, ios_base::openmode _Mode = ios_base::in, int _Prot = ios_base::_Default_open_prot)

And when wchar_t is real, it's also constructible from const unsigned short*:

#ifdef _NATIVE_WCHAR_T_DEFINED
explicit basic_ifstream(const unsigned short* _Filename, ios_base::openmode _Mode = ios_base::in,
int _Prot = ios_base::_Default_open_prot)

I believe that all of our _NATIVE_WCHAR_T_DEFINED logic needs to be audited. When we're separately compiling the STL, wchar_t is always a real type, and we additionally need to provide unsigned short counterparts (so we should detect whether we're building the STL). But when headers are being used to compile user code, users with real wchar_t should see only wchar_t for character-ish things, never unsigned short. (Users with fake wchar_t will see only wchar_t being a typedef for unsigned short, as they should.)

In theory, this should not affect ABI compatibility, although it may affect source compatibility (if users with real wchar_t somehow took a dependency on the unsigned short machinery).

Also tracked by Microsoft-internal VSO-387426 / AB#387426.