[libc++] Use std::to_chars to format thread::id and canonicalize the representation across platforms (#181624)
This makes printing `thread::id` faster, since we avoid a bunch of boilerplate code that isn't optimized away. It also avoids including `<sstream>` and `<__locale>`, cutting the time to parse `<thread>` in half. This also changes the output when printing the id on a platform that uses a pointer as the underlying type. I don't think that's a problem, since the thread id isn't in any way stable. It also makes the output consistent across all platforms we support.
This commit is contained in:
parent
4a02e116e5
commit
a68147f0f0
13 changed files with 170 additions and 194 deletions
|
|
@ -92,6 +92,9 @@ Potentially breaking changes
|
|||
including the correct header. To ease the transition ``_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23`` can be defined, which
|
||||
includes the removed headers again. This macro will be removed in LLVM 24.
|
||||
|
||||
- The output of ``ostream << std::thread::id`` and ``std::format("...", std::thread:id)`` changes on some platforms from
|
||||
hexadecimal to decimal. This is done to have consistent formatting across all platforms libc++ supports.
|
||||
|
||||
Announcements About Future Releases
|
||||
-----------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -51,20 +51,13 @@ public:
|
|||
// On Linux systems pthread_t is an unsigned long long.
|
||||
// On Apple systems pthread_t is a pointer type.
|
||||
//
|
||||
// Note the output should match what the stream operator does. Since
|
||||
// the ostream operator has been shipped years before this formatter
|
||||
// was added to the Standard, this formatter does what the stream
|
||||
// operator does. This may require platform specific changes.
|
||||
// Note the output should match what the stream operator does.
|
||||
|
||||
using _Tp = decltype(__get_underlying_id(__id));
|
||||
using _Cp = conditional_t<integral<_Tp>, _Tp, conditional_t<is_pointer_v<_Tp>, uintptr_t, void>>;
|
||||
static_assert(!is_same_v<_Cp, void>, "unsupported thread::id type, please file a bug report");
|
||||
|
||||
__format_spec::__parsed_specifications<_CharT> __specs = __parser_.__get_parsed_std_specifications(__ctx);
|
||||
if constexpr (is_pointer_v<_Tp>) {
|
||||
__specs.__std_.__alternate_form_ = true;
|
||||
__specs.__std_.__type_ = __format_spec::__type::__hexadecimal_lower_case;
|
||||
}
|
||||
return __formatter::__format_integer(reinterpret_cast<_Cp>(__get_underlying_id(__id)), __ctx, __specs);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,13 @@
|
|||
#define _LIBCPP___THREAD_THREAD_H
|
||||
|
||||
#include <__assert>
|
||||
#include <__charconv/to_chars_integral.h>
|
||||
#include <__condition_variable/condition_variable.h>
|
||||
#include <__config>
|
||||
#include <__exception/terminate.h>
|
||||
#include <__functional/hash.h>
|
||||
#include <__functional/unary_function.h>
|
||||
#include <__locale>
|
||||
#include <__fwd/ostream.h>
|
||||
#include <__memory/addressof.h>
|
||||
#include <__memory/unique_ptr.h>
|
||||
#include <__mutex/mutex.h>
|
||||
|
|
@ -27,16 +28,15 @@
|
|||
#include <__type_traits/enable_if.h>
|
||||
#include <__type_traits/invoke.h>
|
||||
#include <__type_traits/is_constructible.h>
|
||||
#include <__type_traits/is_pointer.h>
|
||||
#include <__type_traits/is_same.h>
|
||||
#include <__type_traits/remove_cvref.h>
|
||||
#include <__utility/exchange.h>
|
||||
#include <__utility/forward.h>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
|
||||
#if _LIBCPP_HAS_LOCALIZATION
|
||||
# include <sstream>
|
||||
#endif
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
|
@ -146,13 +146,19 @@ operator<<(basic_ostream<_CharT, _Traits>& __os, __thread_id __id) {
|
|||
//
|
||||
// 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.
|
||||
// use `to_chars` directly and output the id as a string.
|
||||
|
||||
basic_ostringstream<_CharT, _Traits> __sstr;
|
||||
__sstr.imbue(locale::classic());
|
||||
__sstr << __id.__id_;
|
||||
return __os << __sstr.str();
|
||||
static_assert(is_pointer<__libcpp_thread_id>::value || is_integral<__libcpp_thread_id>::value);
|
||||
|
||||
using __int_type = __conditional_t<is_pointer<__libcpp_thread_id>::value, uintptr_t, __libcpp_thread_id>;
|
||||
|
||||
static const size_t __buffer_size = numeric_limits<__int_type>::digits10 + 2;
|
||||
char __buffer[__buffer_size];
|
||||
auto __ret =
|
||||
std::__to_chars_integral(__buffer, __buffer + __buffer_size - 1, reinterpret_cast<__int_type>(__id.__id_), 10);
|
||||
_LIBCPP_ASSERT_INTERNAL(__ret.__ec == std::errc(), "to_chars failed!");
|
||||
*__ret.__ptr = '\0';
|
||||
return __os << __buffer;
|
||||
}
|
||||
# endif // _LIBCPP_HAS_LOCALIZATION
|
||||
|
||||
|
|
|
|||
|
|
@ -2078,6 +2078,11 @@ _LIBCPP_POP_MACROS
|
|||
# include <system_error>
|
||||
# include <thread>
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 23
|
||||
# include <sstream>
|
||||
# endif
|
||||
|
||||
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
|
||||
|
||||
#endif // _LIBCPP_FUTURE
|
||||
|
|
|
|||
|
|
@ -948,7 +948,10 @@ module std {
|
|||
header "__charconv/to_chars_integral.h"
|
||||
export std.charconv.to_chars_result
|
||||
}
|
||||
module to_chars_result { header "__charconv/to_chars_result.h" }
|
||||
module to_chars_result {
|
||||
header "__charconv/to_chars_result.h"
|
||||
export std.system_error.errc
|
||||
}
|
||||
module traits { header "__charconv/traits.h" }
|
||||
|
||||
header "charconv"
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ void sleep_for(const chrono::duration<Rep, Period>& rel_time);
|
|||
|
||||
# if defined(_LIBCPP_KEEP_TRANSITIVE_INCLUDES_LLVM23) && _LIBCPP_STD_VER <= 23
|
||||
# include <atomic>
|
||||
# include <sstream>
|
||||
# endif
|
||||
|
||||
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: no-threads
|
||||
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO FMT This test should not require std::to_chars(floating-point)
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
|
||||
// <thread>
|
||||
|
||||
// template<class charT>
|
||||
// struct formatter<thread::id, charT>;
|
||||
|
||||
// template<class... Args>
|
||||
// string format(format_string<Args...> fmt, Args&&... args);
|
||||
// template<class... Args>
|
||||
// wstring format(wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <thread>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "format.functions.common.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
template <class CharT, class TestFunction>
|
||||
void format_tests(TestFunction check) {
|
||||
// Note the output of std::thread::id is unspecified. The output text is the
|
||||
// same as the stream operator. Since that format is already released this
|
||||
// test follows the practice on existing systems.
|
||||
std::thread::id input{};
|
||||
|
||||
/***** Test the type specific part *****/
|
||||
check(SV("0"), SV("{}"), input);
|
||||
check(SV("0^42"), SV("{}^42"), input);
|
||||
check(SV("0^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV(" 0"), SV("{:5}"), input);
|
||||
check(SV("0****"), SV("{:*<5}"), input);
|
||||
check(SV("__0__"), SV("{:_^5}"), input);
|
||||
check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
|
||||
|
||||
check(SV(" 0"), SV("{:{}}"), input, 5);
|
||||
check(SV("0****"), SV("{:*<{}}"), input, 5);
|
||||
check(SV("__0__"), SV("{:_^{}}"), input, 5);
|
||||
check(SV("####0"), SV("{:#>{}}"), input, 5);
|
||||
}
|
||||
|
||||
auto test = []<class CharT, class... Args>(
|
||||
std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
|
||||
{
|
||||
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
}
|
||||
{
|
||||
std::basic_string<CharT> out = std::vformat(fmt.get(), std::make_format_args<context_t<CharT>>(args...));
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
format_tests<char>(test);
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
format_tests<wchar_t>(test);
|
||||
#endif
|
||||
|
||||
{ // Check that we print the ID correctly (i.e. library-internal round-tripping works)
|
||||
auto thread_id = std::this_thread::get_id();
|
||||
auto result = std::format("{}", thread_id);
|
||||
auto num = std::stoull(result);
|
||||
using int_t =
|
||||
std::conditional_t<std::is_pointer<std::__libcpp_thread_id>::value, uintptr_t, std::__libcpp_thread_id>;
|
||||
assert(reinterpret_cast<int_t>(__get_underlying_id(std::this_thread::get_id())) == static_cast<int_t>(num));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -423,32 +423,23 @@ functional tuple
|
|||
functional typeinfo
|
||||
functional unordered_map
|
||||
functional version
|
||||
future bitset
|
||||
future cctype
|
||||
future cerrno
|
||||
future climits
|
||||
future clocale
|
||||
future compare
|
||||
future cstddef
|
||||
future cstdint
|
||||
future cstdio
|
||||
future cstdlib
|
||||
future cstring
|
||||
future ctime
|
||||
future cwchar
|
||||
future cwctype
|
||||
future initializer_list
|
||||
future ios
|
||||
future iosfwd
|
||||
future istream
|
||||
future limits
|
||||
future ratio
|
||||
future sstream
|
||||
future stdexcept
|
||||
future streambuf
|
||||
future string
|
||||
future string_view
|
||||
future text_encoding
|
||||
future tuple
|
||||
future typeinfo
|
||||
future version
|
||||
|
|
@ -1043,7 +1034,6 @@ text_encoding stdexcept
|
|||
text_encoding string_view
|
||||
text_encoding version
|
||||
thread array
|
||||
thread bitset
|
||||
thread cctype
|
||||
thread cerrno
|
||||
thread climits
|
||||
|
|
@ -1058,14 +1048,10 @@ thread ctime
|
|||
thread cwchar
|
||||
thread cwctype
|
||||
thread initializer_list
|
||||
thread ios
|
||||
thread iosfwd
|
||||
thread istream
|
||||
thread limits
|
||||
thread ratio
|
||||
thread sstream
|
||||
thread stdexcept
|
||||
thread streambuf
|
||||
thread string
|
||||
thread string_view
|
||||
thread text_encoding
|
||||
|
|
|
|||
|
|
|
@ -1,57 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
|
||||
// UNSUPPORTED: no-threads
|
||||
|
||||
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
|
||||
|
||||
// TODO FMT This test should not require std::to_chars(floating-point)
|
||||
// XFAIL: availability-fp_to_chars-missing
|
||||
|
||||
// <thread>
|
||||
|
||||
// template<class charT>
|
||||
// struct formatter<thread::id, charT>;
|
||||
|
||||
// template<class... Args>
|
||||
// string format(format_string<Args...> fmt, Args&&... args);
|
||||
// template<class... Args>
|
||||
// wstring format(wformat_string<Args...> fmt, Args&&... args);
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "format.functions.tests.h"
|
||||
#include "test_format_string.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test = []<class CharT, class... Args>(
|
||||
std::basic_string_view<CharT> expected, test_format_string<CharT, Args...> fmt, Args&&... args) {
|
||||
std::basic_string<CharT> out = std::format(fmt, std::forward<Args>(args)...);
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
|
||||
auto test_exception = []<class CharT, class... Args>(std::string_view, std::basic_string_view<CharT>, Args&&...) {
|
||||
// After P2216 most exceptions thrown by std::format become ill-formed.
|
||||
// Therefore this tests does nothing.
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
format_tests<char>(test, test_exception);
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
format_tests<wchar_t>(test, test_exception);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
|
||||
#define TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
|
||||
|
||||
#include <thread>
|
||||
|
||||
#include "format.functions.common.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
template <class CharT, class TestFunction, class ExceptionTest>
|
||||
void format_tests(TestFunction check, ExceptionTest check_exception) {
|
||||
// Note the output of std::thread::id is unspecified. The output text is the
|
||||
// same as the stream operator. Since that format is already released this
|
||||
// test follows the practice on existing systems.
|
||||
std::thread::id input{};
|
||||
|
||||
/***** Test the type specific part *****/
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
check(SV("0"), SV("{}"), input);
|
||||
check(SV("0^42"), SV("{}^42"), input);
|
||||
check(SV("0^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV(" 0"), SV("{:5}"), input);
|
||||
check(SV("0****"), SV("{:*<5}"), input);
|
||||
check(SV("__0__"), SV("{:_^5}"), input);
|
||||
check(SV("::::0"), SV("{::>5}"), input); // This is not a range, so : is allowed as fill character.
|
||||
|
||||
check(SV(" 0"), SV("{:{}}"), input, 5);
|
||||
check(SV("0****"), SV("{:*<{}}"), input, 5);
|
||||
check(SV("__0__"), SV("{:_^{}}"), input, 5);
|
||||
check(SV("####0"), SV("{:#>{}}"), input, 5);
|
||||
#else // !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
check(SV("0x0"), SV("{}"), input);
|
||||
check(SV("0x0^42"), SV("{}^42"), input);
|
||||
check(SV("0x0^42"), SV("{:}^42"), input);
|
||||
|
||||
// *** align-fill & width ***
|
||||
check(SV(" 0x0"), SV("{:7}"), input);
|
||||
check(SV("0x0****"), SV("{:*<7}"), input);
|
||||
check(SV("__0x0__"), SV("{:_^7}"), input);
|
||||
check(SV("::::0x0"), SV("{::>7}"), input); // This is not a range, so : is allowed as fill character.
|
||||
|
||||
check(SV(" 0x0"), SV("{:{}}"), input, 7);
|
||||
check(SV("0x0****"), SV("{:*<{}}"), input, 7);
|
||||
check(SV("__0x0__"), SV("{:_^{}}"), input, 7);
|
||||
check(SV("####0x0"), SV("{:#>{}}"), input, 7);
|
||||
#endif // !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
|
||||
/***** Test the type generic part *****/
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
|
||||
check_exception("The replacement field misses a terminating '}'", fmt, input);
|
||||
}
|
||||
|
||||
#endif // TEST_STD_THREAD_THREAD_THREADS_THREAD_THREAD_CLASS_THREAD_THREAD_ID_FORMAT_FUNCTIONS_TESTS_H
|
||||
|
|
@ -22,21 +22,47 @@
|
|||
// string vformat(string_view fmt, format_args args);
|
||||
// wstring vformat(wstring_view fmt, wformat_args args);
|
||||
|
||||
#include <format>
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
#include <thread>
|
||||
|
||||
#include "assert_macros.h"
|
||||
#include "concat_macros.h"
|
||||
#include "format.functions.tests.h"
|
||||
#include "format.functions.common.h"
|
||||
#include "test_macros.h"
|
||||
|
||||
auto test = []<class CharT, class... Args>(
|
||||
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) {
|
||||
std::basic_string<CharT> out = std::vformat(fmt, std::make_format_args<context_t<CharT>>(args...));
|
||||
TEST_REQUIRE(out == expected,
|
||||
TEST_WRITE_CONCATENATED(
|
||||
"\nFormat string ", fmt, "\nExpected output ", expected, "\nActual output ", out, '\n'));
|
||||
};
|
||||
template <class CharT, class ExceptionTest>
|
||||
void format_tests(ExceptionTest check_exception) {
|
||||
// Note the output of std::thread::id is unspecified. The output text is the
|
||||
// same as the stream operator. Since that format is already released this
|
||||
// test follows the practice on existing systems.
|
||||
std::thread::id input{};
|
||||
|
||||
/***** Test the type generic part *****/
|
||||
check_exception("The format string contains an invalid escape sequence", SV("{:}<}"), input);
|
||||
check_exception("The fill option contains an invalid value", SV("{:{<}"), input);
|
||||
|
||||
// *** sign ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:-}"), input);
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:+}"), input);
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{: }"), input);
|
||||
|
||||
// *** alternate form ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:#}"), input);
|
||||
|
||||
// *** zero-padding ***
|
||||
check_exception("The width option should not have a leading zero", SV("{:0}"), input);
|
||||
|
||||
// *** precision ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:.}"), input);
|
||||
|
||||
// *** locale-specific form ***
|
||||
check_exception("The replacement field misses a terminating '}'", SV("{:L}"), input);
|
||||
|
||||
// *** type ***
|
||||
for (std::basic_string_view<CharT> fmt : fmt_invalid_types<CharT>(""))
|
||||
check_exception("The replacement field misses a terminating '}'", fmt, input);
|
||||
}
|
||||
|
||||
auto test_exception =
|
||||
[]<class CharT, class... Args>(
|
||||
|
|
@ -55,10 +81,10 @@ auto test_exception =
|
|||
};
|
||||
|
||||
int main(int, char**) {
|
||||
format_tests<char>(test, test_exception);
|
||||
format_tests<char>(test_exception);
|
||||
|
||||
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
|
||||
format_tests<wchar_t>(test, test_exception);
|
||||
format_tests<wchar_t>(test_exception);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -34,9 +34,8 @@
|
|||
|
||||
#define SV(S) MAKE_STRING_VIEW(CharT, S)
|
||||
|
||||
template <class StringViewT>
|
||||
void test_format(StringViewT expected, std::thread::id arg) {
|
||||
using CharT = typename StringViewT::value_type;
|
||||
template <class CharT>
|
||||
std::basic_string<CharT> test_format(std::thread::id arg) {
|
||||
using String = std::basic_string<CharT>;
|
||||
using OutIt = std::back_insert_iterator<String>;
|
||||
using FormatCtxT = std::basic_format_context<OutIt, CharT>;
|
||||
|
|
@ -47,16 +46,15 @@ void test_format(StringViewT expected, std::thread::id arg) {
|
|||
OutIt out = std::back_inserter(result);
|
||||
FormatCtxT format_ctx = test_format_context_create<OutIt, CharT>(out, std::make_format_args<FormatCtxT>(arg));
|
||||
formatter.format(arg, format_ctx);
|
||||
assert(result == expected);
|
||||
assert(!result.empty());
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
void test_fmt() {
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||
test_format(SV("0"), std::thread::id());
|
||||
#else
|
||||
test_format(SV("0x0"), std::thread::id());
|
||||
#endif
|
||||
auto def = test_format<CharT>(std::thread::id());
|
||||
auto self = test_format<CharT>(std::this_thread::get_id());
|
||||
assert(def != self);
|
||||
}
|
||||
|
||||
void test() {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <format>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "make_string.h"
|
||||
|
|
|
|||
Loading…
Reference in a new issue