diff --git a/libcxx/include/__thread/thread.h b/libcxx/include/__thread/thread.h --- a/libcxx/include/__thread/thread.h +++ b/libcxx/include/__thread/thread.h @@ -24,6 +24,10 @@ #include #include +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +# include +#endif + #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif @@ -126,11 +130,32 @@ } }; -template -_LIBCPP_INLINE_VISIBILITY -basic_ostream<_CharT, _Traits>& -operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) -{return __os << __id.__id_;} +#ifndef _LIBCPP_HAS_NO_LOCALIZATION +template +_LIBCPP_INLINE_VISIBILITY basic_ostream<_CharT, _Traits>& +operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) { + // [thread.thread.id]/9 + // Effects: Inserts the text representation for charT of id into out. + // + // [thread.thread.id]/2 + // The text representation for the character type charT of an + // object of type thread::id is an unspecified sequence of charT + // such that, for two objects of type thread::id x and y, if + // x == y is true, the thread::id objects have the same text + // representation, and if x != y is true, the thread::id objects + // have distinct text representations. + // + // Since various flags in the output stream can affect how the + // thread id is represented (e.g. numpunct or showbase), we + // use a temporary stream instead and just output the thread + // id representation as a string. + + basic_ostringstream<_CharT, _Traits> __sstr; + __sstr.imbue(locale::classic()); + __sstr << __id.__id_; + return __os << __sstr.str(); +} +#endif // _LIBCPP_HAS_NO_LOCALIZATION class _LIBCPP_EXPORTED_FROM_ABI thread { diff --git a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp --- a/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp +++ b/libcxx/test/std/thread/thread.threads/thread.thread.class/thread.thread.id/stream.pass.cpp @@ -10,6 +10,8 @@ // UNSUPPORTED: no-localization // UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME +// REQUIRES: locale.fr_FR.UTF-8 + // TODO FMT This test should not require std::to_chars(floating-point) // XFAIL: availability-fp_to_chars-missing @@ -21,16 +23,18 @@ // basic_ostream& // operator<<(basic_ostream& out, thread::id id); -#include +#include #include +#include #include -#include +#include #include "make_string.h" +#include "platform_support.h" // locale name macros #include "test_macros.h" template -static void test() { +static void basic() { std::thread::id id0 = std::this_thread::get_id(); std::basic_ostringstream os; os << id0; @@ -49,6 +53,91 @@ #endif } +template +static std::basic_string format(std::ios_base::fmtflags flags) { + std::basic_stringstream sstr; + sstr.flags(flags); + sstr << std::this_thread::get_id(); + return sstr.str(); +} + +template +static void stream_state() { + std::basic_stringstream sstr; + sstr << std::this_thread::get_id(); + std::basic_string expected = sstr.str(); + + // Unaffected by fill, width, and align. + + assert(expected == format(std::ios_base::dec | std::ios_base::skipws)); // default flags + + assert(expected == format(std::ios_base::oct)); + assert(expected == format(std::ios_base::hex)); + + assert(expected == format(std::ios_base::scientific)); + assert(expected == format(std::ios_base::fixed)); + + assert(expected == format(std::ios_base::boolalpha)); + assert(expected == format(std::ios_base::showbase)); + assert(expected == format(std::ios_base::showpoint)); + assert(expected == format(std::ios_base::showpos)); + assert(expected == format(std::ios_base::skipws)); // added for completeness + assert(expected == format(std::ios_base::unitbuf)); // added for completeness + assert(expected == format(std::ios_base::uppercase)); + + // Test fill, width, and align. + + sstr.str(std::basic_string()); + sstr.fill(CharT('#')); + sstr.width(expected.size() + 10); // Make sure fill and align affect the output. + sstr.flags(std::ios_base::dec | std::ios_base::skipws | std::ios_base::right); + sstr << std::this_thread::get_id(); + expected = sstr.str(); + + sstr.str(std::basic_string()); + sstr.fill(CharT('*')); + sstr.width(expected.size()); + sstr.flags(std::ios_base::dec | std::ios_base::skipws | std::ios_base::right); + sstr << std::this_thread::get_id(); + assert(expected != sstr.str()); + + sstr.str(std::basic_string()); + sstr.fill(CharT('#')); + sstr.width(expected.size() - 1); + sstr.flags(std::ios_base::dec | std::ios_base::skipws | std::ios_base::right); + sstr << std::this_thread::get_id(); + assert(expected != sstr.str()); + + sstr.str(std::basic_string()); + sstr.fill(CharT('#')); + sstr.width(expected.size()); + sstr.flags(std::ios_base::dec | std::ios_base::skipws | std::ios_base::left); + sstr << std::this_thread::get_id(); + assert(expected != sstr.str()); + + sstr.str(std::basic_string()); + sstr.fill(CharT('#')); + sstr.width(expected.size()); + sstr.flags(std::ios_base::dec | std::ios_base::skipws | std::ios_base::internal); + sstr << std::this_thread::get_id(); + assert(expected == sstr.str()); // internal does *not* affect strings + + // Test the locale's numpunct. + + std::locale::global(std::locale(LOCALE_fr_FR_UTF_8)); + sstr.str(std::basic_string()); + sstr.fill(CharT('#')); + sstr.width(expected.size()); + sstr << std::this_thread::get_id(); + assert(expected == sstr.str()); +} + +template +static void test() { + basic(); + stream_state(); +} + int main(int, char**) { test(); #ifndef TEST_HAS_NO_WIDE_CHARACTERS