Skip to content

[libc++] Implements Runtime format strings. #73353

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
- P2905R2 - Runtime format strings
- P2918R2 - Runtime format strings II
- P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
- P2870R3 - Remove basic_string::reserve()
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
"`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|"
"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
"`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, where’s my ``char``?)","Kona November 2023","|Complete|","18.0","|format| |DR|"
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/FormatIssues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Number,Name,Standard,Assignee,Status,First released version
"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|",17.0
"`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|"
"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|Complete|",18.0
"`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, where’s my ``char``?)","C++26 DR","Mark de Wever","|Complete|",18.0
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,
Expand Down
6 changes: 3 additions & 3 deletions libcxx/include/__format/format_arg_store.h
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu
}

template <class _Context, class... _Args>
_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
_Args&&... __args) noexcept {
_LIBCPP_HIDE_FROM_ABI void
__create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept {
int __shift = 0;
(
[&] {
Expand All @@ -224,7 +224,7 @@ _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_fo
}

template <class _Context, class... _Args>
_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&&... __args) noexcept {
_LIBCPP_HIDE_FROM_ABI void __store_basic_format_arg(basic_format_arg<_Context>* __data, _Args&... __args) noexcept {
([&] { *__data++ = __format::__create_format_arg<_Context>(__args); }(), ...);
}

Expand Down
8 changes: 4 additions & 4 deletions libcxx/include/__format/format_functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ using wformat_args = basic_format_args<wformat_context>;
#endif

template <class _Context = format_context, class... _Args>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&&... __args) {
return std::__format_arg_store<_Context, _Args...>(__args...);
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&... __args) {
return _VSTD::__format_arg_store<_Context, _Args...>(__args...);
}

# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
template <class... _Args>
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
make_wformat_args(_Args&&... __args) {
return std::__format_arg_store<wformat_context, _Args...>(__args...);
make_wformat_args(_Args&... __args) {
return _VSTD::__format_arg_store<wformat_context, _Args...>(__args...);
}
# endif

Expand Down
4 changes: 2 additions & 2 deletions libcxx/include/format
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ namespace std {

template<class Context = format_context, class... Args>
format-arg-store<Context, Args...>
make_format_args(Args&&... args);
make_format_args(Args&... args);
template<class... Args>
format-arg-store<wformat_context, Args...>
make_wformat_args(Args&&... args);
make_wformat_args(Args&... args);

// [format.error], class format_error
class format_error;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ static void test_vprint_unicode() {
FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
assert(file);

std::vprint_unicode(file, "hello world{}", std::make_format_args('!'));
char c = '!';
std::vprint_unicode(file, "hello world{}", std::make_format_args(c));
long pos = std::ftell(file);
std::fclose(file);

Expand All @@ -83,7 +84,8 @@ static void test_vprint_nonunicode() {
FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
assert(file);

std::vprint_nonunicode(file, "hello world{}", std::make_format_args('!'));
char c = '!';
std::vprint_nonunicode(file, "hello world{}", std::make_format_args(c));
long pos = std::ftell(file);
std::fclose(file);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@

int main(int, char**) {
// The data is passed as-is so it does not depend on the encoding of the input.
std::vprint_nonunicode("{} {} ", std::make_format_args(1234, "一二三四"));
std::vprint_nonunicode("{} {}", std::make_format_args(true, nullptr));
int i = 1234;
const char* s = "一二三四";
bool b = true;
nullptr_t p = nullptr;
std::vprint_nonunicode("{} {} ", std::make_format_args(i, s));
std::vprint_nonunicode("{} {}", std::make_format_args(b, p));

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@

int main(int, char**) {
// The data is passed as-is so it does not depend on the encoding of the input.
std::vprint_unicode("{} {} ", std::make_format_args(1234, "一二三四"));
std::vprint_unicode("{} {}", std::make_format_args(true, nullptr));
int i = 1234;
const char* s = "一二三四";
bool b = true;
nullptr_t p = nullptr;
std::vprint_unicode("{} {} ", std::make_format_args(i, s));
std::vprint_unicode("{} {}", std::make_format_args(b, p));

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// <format>

// template<class Context = format_context, class... Args>
// format-arg-store<Context, Args...> make_format_args(const Args&... args);
// format-arg-store<Context, Args...> make_format_args(Args&... args);

#include <cassert>
#include <format>
Expand All @@ -20,8 +20,19 @@
#include "test_basic_format_arg.h"
#include "test_macros.h"

template <class... Args>
concept can_make_format_args = requires(Args&&... args) { std::make_format_args(std::forward<Args>(args)...); };

static_assert(can_make_format_args<int&>);
static_assert(!can_make_format_args<int>);
static_assert(!can_make_format_args<int&&>);

int main(int, char**) {
[[maybe_unused]] auto store = std::make_format_args(42, nullptr, false, 'x');
int i = 1;
char c = 'c';
nullptr_t p = nullptr;
bool b = false;
[[maybe_unused]] auto store = std::make_format_args(i, p, b, c);

LIBCPP_STATIC_ASSERT(
std::same_as<decltype(store), std::__format_arg_store<std::format_context, int, nullptr_t, bool, char>>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "test_macros.h"

void test() {
char c = 'c';
TEST_IGNORE_NODISCARD
std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>('c');
std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>(c);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,27 @@

// template<class... Args>
// format-arg-store<wformat_context, Args...>
// make_wformat_args(const Args&... args);
// make_wformat_args(Args&... args);

#include <cassert>
#include <format>

#include "test_basic_format_arg.h"
#include "test_macros.h"

template <class... Args>
concept can_make_wformat_args = requires(Args&&... args) { std::make_wformat_args(std::forward<Args>(args)...); };

static_assert(can_make_wformat_args<int&>);
static_assert(!can_make_wformat_args<int>);
static_assert(!can_make_wformat_args<int&&>);

int main(int, char**) {
[[maybe_unused]] auto store = std::make_wformat_args(42, nullptr, false, 'x');
int i = 1;
char c = 'c';
nullptr_t p = nullptr;
bool b = false;
[[maybe_unused]] auto store = std::make_wformat_args(i, p, b, c);

LIBCPP_STATIC_ASSERT(
std::same_as<decltype(store), std::__format_arg_store<std::wformat_context, int, nullptr_t, bool, char>>);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
#include "test_macros.h"

void test() {
int i = 1;
// Note the Standard way to create a format-arg-store is by using make_format_args.
static_assert(std::same_as<decltype(std::basic_format_args(std::make_format_args(42))),
static_assert(std::same_as<decltype(std::basic_format_args(std::make_format_args(i))),
std::basic_format_args<std::format_context>>);

#ifndef TEST_HAS_NO_WIDE_CHARACTERS
static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(42))),
static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(i))),
std::basic_format_args<std::wformat_context>>);

#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

template <class CharT>
void test() {
int i = 1;
char c = 'c';
nullptr_t p = nullptr;
using Context = std::basic_format_context<CharT*, CharT>;
{
ASSERT_NOEXCEPT(std::basic_format_args<Context>{});
Expand All @@ -28,22 +31,22 @@ void test() {
assert(!format_args.get(0));
}
{
auto store = std::make_format_args<Context>(1);
auto store = std::make_format_args<Context>(i);
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
std::basic_format_args<Context> format_args{store};
assert(format_args.get(0));
assert(!format_args.get(1));
}
{
auto store = std::make_format_args<Context>(1, 'c');
auto store = std::make_format_args<Context>(i, c);
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
std::basic_format_args<Context> format_args{store};
assert(format_args.get(0));
assert(format_args.get(1));
assert(!format_args.get(2));
}
{
auto store = std::make_format_args<Context>(1, 'c', nullptr);
auto store = std::make_format_args<Context>(i, c, p);
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
std::basic_format_args<Context> format_args{store};
assert(format_args.get(0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@

template <class OutIt, class CharT>
void test() {
bool b = true;
CharT c = CharT('a');
int a = 42;
std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
auto store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(
true, CharT('a'), 42, string);
auto store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
std::basic_format_args args = store;

std::basic_string<CharT> output;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,14 @@

template <class OutIt, class CharT>
void test() {
int a = 42;
bool b = true;
CharT c = CharT('a');
std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
// The type of the object is an exposition only type. The temporary is needed
// to extend the lifetime of the object since args stores a pointer to the
// data in this object.
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
std::basic_format_args args = format_arg_store;

{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ void test() {
// The type of the object is an exposition only type. The temporary is needed
// to extend the lifetime of the object since args stores a pointer to the
// data in this object.
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
int a = 42;
bool b = true;
CharT c = CharT('a');
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
std::basic_format_args args = format_arg_store;

{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ void test(StringT expected, StringViewT fmt, StringT a, std::size_t offset) {
using FormatCtxT = std::basic_format_context<decltype(out), CharT>;

ArgumentT arg = a;
FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(
out, std::make_format_args<FormatCtxT>(std::forward<ArgumentT>(arg)));
FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(out, std::make_format_args<FormatCtxT>(arg));
formatter.format(arg, format_ctx);
assert(result == expected);
}
Expand Down