4

Following code compiles fine on VS 2015(Update 3) and gcc 6.3 (C++14) without on any problems.

#include <string>
#include <locale>

int main()
{
    std::u16string ustr = u"Android";

    bool var = std::isspace(ustr[0],std::locale());

    return 0;
}

However, on clang/Xcode it fails with following error

Error(s):
source_file.cpp:8:10: warning: unused variable 'var' [-Wunused-variable]
    bool var = std::isspace(ustr[0],std::locale());
         ^
In file included from source_file.cpp:2:
In file included from /usr/include/c++/v1/locale:182:
/usr/include/c++/v1/__locale:705:44: error: implicit instantiation of undefined template 'std::__1::ctype<char16_t>'
    return use_facet<ctype<_CharT> >(__loc).is(ctype_base::space, __c);
                                           ^
source_file.cpp:8:21: note: in instantiation of function template specialization 'std::__1::isspace<char16_t>' requested here
    bool var = std::isspace(ustr[0],std::locale());
                    ^
/usr/include/c++/v1/__locale:427:53: note: template is declared here
template <class _CharT> class _LIBCPP_TYPE_VIS_ONLY ctype;
                                                    ^
/usr/include/c++/v1/__locale:186:54: error: implicit instantiation of undefined template 'std::__1::ctype<char16_t>'
    return static_cast<const _Facet&>(*__l.use_facet(_Facet::id));
                                                     ^
/usr/include/c++/v1/__locale:705:12: note: in instantiation of function template specialization 'std::__1::use_facet<std::__1::ctype<char16_t> >' requested here
    return use_facet<ctype<_CharT> >(__loc).is(ctype_base::space, __c);
           ^
source_file.cpp:8:21: note: in instantiation of function template specialization 'std::__1::isspace<char16_t>' requested here
    bool var = std::isspace(ustr[0],std::locale());
                    ^
/usr/include/c++/v1/__locale:427:53: note: template is declared here
template <class _CharT> class _LIBCPP_TYPE_VIS_ONLY ctype;
                                                    ^
1 warning and 2 errors generated.

What am I missing here ? Any header inclusion ? If so, which one is it ? If not, any other workarounds to solve this ?

Recker
  • 1,915
  • 25
  • 55
  • 1
    Defined in header --> int isspace( int ch ), this function only accepts one parameter – FrankS101 Jan 16 '18 at 12:25
  • 1
    @FrankS101 OP is using the overload defined in `locale`. – Holt Jan 16 '18 at 12:27
  • 1
    I think the only "defined" solution would be to write one's own trait for it, since it's not already defined. Maybe a library defect, but it's debatable. – AndyG Jan 16 '18 at 12:50

2 Answers2

7

The std::isspace overload in <locale> is equivalent (per the standard) to:

std::use_facet< std::ctype<charT> >(loc).is(ctype_base::space, c)

As you can see, this requires a specialization of std::ctype for the given CharT.

The standard only provides std::ctype<char> and std::ctype<wchar_t>. Since char16_t is a built-in type, you cannot specialize std::ctype<char16_t> so I think you are screwed here.

Holt
  • 36,600
  • 7
  • 92
  • 139
  • Unfortunately I think it is technically undefined behavior to provide a template specialization for a built-in type within the std namespace – AndyG Jan 16 '18 at 12:48
  • @AndyG You're right, it's a flaw in the standard then, unless I'm missing something. – Holt Jan 16 '18 at 13:06
5

The problem is that the std::locale implementation of std::isspace uses character traits defined in std::ctype, which define certain features of a character type.

Unfortunately the standard only requires that std::ctype is specialized for char and wchar_t, not char16_t like you need. It appears that MSVC and GCC are providing extra implementations (because it would make sense). (EDIT: GCC actually throws an exception).

We cannot add our own specialization for it because [namespace.std] states that we may only add specializations for user-defined types to namespace std.

This leaves us with a few options:

  • Cast your characters to char or wchar_t before using std::isspace from <locale>
    • note that for char this will often be a narrowing conversion which you likely do not want
  • switch to using the implementation of std::isspace from <cctype> which casts to an int.
    • if the type cannot be represented within an unsigned char you have undefined behavior, so this may also be not what you want depending on which type you use (check against sizeof(unsigned char))
  • write your own trait for your character type (probably the best choice right now)
  • file a defect report, hope the committee agrees with you, and then wait for two years or more before it's fixed :-)
    • alternatively ask the clang writers to add this convenience (also not a bad idea)
  • Write your own specialization like @Holt did and just live with undefined behavior

Relevant: How well is Unicode supported in C++11?

AndyG
  • 39,700
  • 8
  • 109
  • 143