[libc++] Implements Runtime format strings II. (#72543)

Implements
- P2918R2 Runtime format strings II
This commit is contained in:
Mark de Wever 2023-11-24 17:30:33 +01:00 committed by GitHub
parent 76c4a6e310
commit 92d9f232dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 334 additions and 3 deletions

View File

@ -50,6 +50,7 @@ Implemented Papers
- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
- P2467R1 - Support exclusive mode for fstreams
- P0020R6 - Floating Point Atomic
- P2918R2 - Runtime format strings II
Improvements and New Features

View File

@ -31,7 +31,7 @@
"`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
"`P2546R5 <https://wg21.link/P2546R5>`__","LWG","Debugging Support","Kona November 2023","","",""
"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","","","|format| |DR|"
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","","","|format|"
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
"`P2909R4 <https://wg21.link/P2909R4>`__","LWG","Fix formatting of code units as integers (Dude, wheres my ``char``?)","Kona November 2023","","","|format| |DR|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
"`P2447R6 <https://wg21.link/P2447R6>`__","LWG","``std::span`` over an initializer list","Kona November 2023","","",""

1 Paper # Group Paper Name Meeting Status First released version Labels
31 `P2407R5 <https://wg21.link/P2407R5>`__ LWG Freestanding Library: Partial Classes Kona November 2023
32 `P2546R5 <https://wg21.link/P2546R5>`__ LWG Debugging Support Kona November 2023
33 `P2905R2 <https://wg21.link/P2905R2>`__ LWG Runtime format strings Kona November 2023 |format| |DR|
34 `P2918R2 <https://wg21.link/P2918R2>`__ LWG Runtime format strings II Kona November 2023 |Complete| 18.0 |format|
35 `P2909R4 <https://wg21.link/P2909R4>`__ LWG Fix formatting of code units as integers (Dude, where’s my ``char``?) Kona November 2023 |format| |DR|
36 `P0952R2 <https://wg21.link/P0952R2>`__ LWG A new specification for ``std::generate_canonical`` Kona November 2023
37 `P2447R6 <https://wg21.link/P2447R6>`__ LWG ``std::span`` over an initializer list Kona November 2023

View File

@ -18,7 +18,7 @@ Number,Name,Standard,Assignee,Status,First released version
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","C++26","","",
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","","",
"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|In Progress|"
"`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|In Progress|"
"`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|Complete|",18.0
"`P2909R4 <https://wg21.link/P2909R4>`__","Fix formatting of code units as integers (Dude, wheres my ``char``?)","C++26 DR","Mark de Wever","|In Progress|"
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
`P2372 <https://wg21.link/P2372>`__,"Fixing locale handling in chrono formatters","C++20",Mark de Wever,|In Progress|,

Can't render this file because it has a wrong number of fields in line 8.

View File

@ -338,6 +338,30 @@ __vformat_to(_ParseCtx&& __parse_ctx, _Ctx&& __ctx) {
} // namespace __format
# if _LIBCPP_STD_VER >= 26
template <class _CharT>
struct _LIBCPP_TEMPLATE_VIS __runtime_format_string {
private:
basic_string_view<_CharT> __str_;
template <class _Cp, class... _Args>
friend struct _LIBCPP_TEMPLATE_VIS basic_format_string;
public:
_LIBCPP_HIDE_FROM_ABI __runtime_format_string(basic_string_view<_CharT> __s) noexcept : __str_(__s) {}
__runtime_format_string(const __runtime_format_string&) = delete;
__runtime_format_string& operator=(const __runtime_format_string&) = delete;
};
_LIBCPP_HIDE_FROM_ABI inline __runtime_format_string<char> runtime_format(string_view __fmt) noexcept { return __fmt; }
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
_LIBCPP_HIDE_FROM_ABI inline __runtime_format_string<wchar_t> runtime_format(wstring_view __fmt) noexcept {
return __fmt;
}
# endif
# endif //_LIBCPP_STD_VER >= 26
template <class _CharT, class... _Args>
struct _LIBCPP_TEMPLATE_VIS basic_format_string {
template <class _Tp>
@ -350,6 +374,9 @@ struct _LIBCPP_TEMPLATE_VIS basic_format_string {
_LIBCPP_HIDE_FROM_ABI constexpr basic_string_view<_CharT> get() const noexcept {
return __str_;
}
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI basic_format_string(__runtime_format_string<_CharT> __s) noexcept : __str_(__s.__str_) {}
# endif
private:
basic_string_view<_CharT> __str_;

View File

@ -31,6 +31,7 @@ namespace std {
public:
template<class T> consteval basic_format_string(const T& s);
basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {} // since C++26
constexpr basic_string_view<charT> get() const noexcept { return str; }
};
@ -41,6 +42,24 @@ namespace std {
using wformat_string = // since C++23, exposition only before C++23
basic_format_string<wchar_t, type_identity_t<Args>...>;
template<class charT> struct runtime-format-string { // since C++26, exposition-only
private:
basic_string_view<charT> str; // exposition-only
public:
runtime-format-string(basic_string_view<charT> s) noexcept : str(s) {}
runtime-format-string(const runtime-format-string&) = delete;
runtime-format-string& operator=(const runtime-format-string&) = delete;
};
runtime-format-string<char> runtime_format(string_view fmt) noexcept {
return fmt;
}
runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept {
return fmt;
}
// [format.functions], formatting functions
template<class... Args>
string format(format-string<Args...> fmt, Args&&... args);

View File

@ -28,6 +28,9 @@ export namespace std {
#ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
using std::wformat_string;
#endif
#if _LIBCPP_STD_VER >= 26
using std::runtime_format;
#endif //_LIBCPP_STD_VER >= 26
// [format.functions], formatting functions
using std::format;

View File

@ -0,0 +1,43 @@
//===----------------------------------------------------------------------===//
//
// 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, c++23
// <format>
// template<class charT, class... Args>
// class basic_format_string<charT, type_identity_t<Args>...>
//
// basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {}
//
// Additional testing is done in
// - libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
// - libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
#include <format>
#include <cassert>
#include "test_macros.h"
int main(int, char**) {
static_assert(noexcept(std::format_string<>{std::runtime_format(std::string_view{})}));
{
std::format_string<> s = std::runtime_format("}{invalid format string}{");
assert(s.get() == "}{invalid format string}{");
}
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(noexcept(std::wformat_string<>{std::runtime_format(std::wstring_view{})}));
{
std::wformat_string<> s = std::runtime_format(L"}{invalid format string}{");
assert(s.get() == L"}{invalid format string}{");
}
#endif // TEST_HAS_NO_WIDE_CHARACTERS
return 0;
}

View File

@ -0,0 +1,85 @@
//===----------------------------------------------------------------------===//
// 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, c++23
// UNSUPPORTED: no-localization
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
// XFAIL: availability-fp_to_chars-missing
// <format>
// Tests the behavior of
//
// runtime-format-string<char> runtime_format(string_view fmt) noexcept;
// runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept;
//
// and
//
// template<class charT, class... Args>
// struct basic_format_string {
// ...
// basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {}
// ...
// }
//
// This is done by testing it in the top-level functions:
//
// template<class... Args>
// string format(const locale& loc, format_string<Args...> fmt, Args&&... args);
// template<class... Args>
// wstring format(const locale& loc, wformat_string<Args...> fmt, Args&&... args);
//
// The basics of runtime_format and basic_format_string's constructor are tested in
// - libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
// - libcxx/test/std/utilities/format/format.fmt.string/ctor.runtime-format-string.pass.cpp
#include <format>
#include <cassert>
#include <locale>
#include <vector>
#include "test_macros.h"
#include "format_tests.h"
#include "string_literal.h"
#include "assert_macros.h"
#include "concat_macros.h"
auto test = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) constexpr {
std::basic_string<CharT> out = std::format(std::locale(), std::runtime_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>(
[[maybe_unused]] std::string_view what,
[[maybe_unused]] std::basic_string_view<CharT> fmt,
[[maybe_unused]] Args&&... args) {
TEST_VALIDATE_EXCEPTION(
std::format_error,
[&]([[maybe_unused]] const std::format_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED(
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::format(std::locale(), std::runtime_format(fmt), std::forward<Args>(args)...));
};
int main(int, char**) {
format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
}

View File

@ -0,0 +1,83 @@
//===----------------------------------------------------------------------===//
// 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, c++23
// UNSUPPORTED: GCC-ALWAYS_INLINE-FIXME
// XFAIL: availability-fp_to_chars-missing
// <format>
// Tests the behavior of
//
// runtime-format-string<char> runtime_format(string_view fmt) noexcept;
// runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept;
//
// and
//
// template<class charT, class... Args>
// struct basic_format_string {
// ...
// basic_format_string(runtime-format-string<charT> s) noexcept : str(s.str) {}
// ...
// }
//
// This is done by testing it in the top-level functions:
//
// template<class... Args>
// string format(format_string<Args...> fmt, Args&&... args);
// template<class... Args>
// wstring format(wformat_string<Args...> fmt, Args&&... args);
//
// The basics of runtime_format and basic_format_string's constructor are tested in
// - libcxx/test/std/utilities/format/format.syn/runtime_format_string.pass.cpp
// - libcxx/test/std/utilities/format/format.fmt.string/ctor.runtime-format-string.pass.cpp
#include <format>
#include <cassert>
#include <vector>
#include "test_macros.h"
#include "format_tests.h"
#include "string_literal.h"
#include "assert_macros.h"
#include "concat_macros.h"
auto test = []<class CharT, class... Args>(
std::basic_string_view<CharT> expected, std::basic_string_view<CharT> fmt, Args&&... args) constexpr {
std::basic_string<CharT> out = std::format(std::runtime_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>(
[[maybe_unused]] std::string_view what,
[[maybe_unused]] std::basic_string_view<CharT> fmt,
[[maybe_unused]] Args&&... args) {
TEST_VALIDATE_EXCEPTION(
std::format_error,
[&]([[maybe_unused]] const std::format_error& e) {
TEST_LIBCPP_REQUIRE(
e.what() == what,
TEST_WRITE_CONCATENATED(
"\nFormat string ", fmt, "\nExpected exception ", what, "\nActual exception ", e.what(), '\n'));
},
TEST_IGNORE_NODISCARD std::format(std::runtime_format(fmt), std::forward<Args>(args)...));
};
int main(int, char**) {
format_tests<char, execution_modus::partial>(test, test_exception);
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
format_tests_char_to_wchar_t(test);
format_tests<wchar_t, execution_modus::partial>(test, test_exception);
#endif
return 0;
}

View File

@ -0,0 +1,70 @@
//===----------------------------------------------------------------------===//
//
// 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, c++23
// <format>
// template<class charT> struct runtime-format-string { // exposition-only
// private:
// basic_string_view<charT> str; // exposition-only
//
// public:
// runtime-format-string(basic_string_view<charT> s) noexcept : str(s) {}
//
// runtime-format-string(const runtime-format-string&) = delete;
// runtime-format-string& operator=(const runtime-format-string&) = delete;
// };
//
// runtime-format-string<char> runtime_format(string_view fmt) noexcept;
// runtime-format-string<wchar_t> runtime_format(wstring_view fmt) noexcept;
//
// Additional testing is done in
// - libcxx/test/std/utilities/format/format.functions/format.runtime_format.pass.cpp
// - libcxx/test/std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp
#include <format>
#include <cassert>
#include <concepts>
#include <string_view>
#include <type_traits>
#include "test_macros.h"
template <class T, class CharT>
static void test_properties() {
static_assert(std::is_nothrow_convertible_v<std::basic_string_view<CharT>, T>);
static_assert(std::is_nothrow_constructible_v<T, std::basic_string_view<CharT>>);
static_assert(!std::copy_constructible<T>);
static_assert(!std::is_copy_assignable_v<T>);
static_assert(!std::move_constructible<T>);
static_assert(!std::is_move_assignable_v<T>);
}
int main(int, char**) {
static_assert(noexcept(std::runtime_format(std::string_view{})));
auto format_string = std::runtime_format(std::string_view{});
using FormatString = decltype(format_string);
LIBCPP_ASSERT((std::same_as<FormatString, std::__runtime_format_string<char>>));
test_properties<FormatString, char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(noexcept(std::runtime_format(std::wstring_view{})));
auto wformat_string = std::runtime_format(std::wstring_view{});
using WFormatString = decltype(wformat_string);
LIBCPP_ASSERT((std::same_as<WFormatString, std::__runtime_format_string<wchar_t>>));
test_properties<WFormatString, wchar_t>();
#endif // TEST_HAS_NO_WIDE_CHARACTERS
return 0;
}

View File

@ -467,7 +467,7 @@ feature_test_macros = [
# "c++20": 202110 Not implemented P2372R3 Fixing locale handling in chrono formatters
"c++20": 202106,
# "c++23": 202207, Not implemented P2419R2 Clarify handling of encodings in localized formatting of chrono types
# "c++26": 202311, Not implemented P2918R2 Runtime format strings II
# "c++26": 202311, P2918R2 Runtime format strings II (implemented)
},
# Note these three papers are adopted at the June 2023 meeting and have sequential numbering
# 202304 P2510R3 Formatting pointers (Implemented)