Skip to content

Commit e3f154d

Browse files
authored
[libc++] Implements Runtime format strings. (#73353)
This change requires quite a number of changes in the tests; this is not code I expect people to use in the wild. So I don't expect breakage for users. Implements: - P2905R2 Runtime format strings, as a Defect Report
1 parent 3ec6c72 commit e3f154d

File tree

18 files changed

+78
-33
lines changed

18 files changed

+78
-33
lines changed

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Implemented Papers
5050
- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
5151
- P2467R1 - Support exclusive mode for fstreams
5252
- P0020R6 - Floating Point Atomic
53+
- P2905R2 - Runtime format strings
5354
- P2918R2 - Runtime format strings II
5455
- P2871R3 - Remove Deprecated Unicode Conversion Facets from C++26
5556
- P2870R3 - Remove basic_string::reserve()

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"`P0543R3 <https://wg21.link/P0543R3>`__","LWG","Saturation arithmetic","Kona November 2023","","",""
3131
"`P2407R5 <https://wg21.link/P2407R5>`__","LWG","Freestanding Library: Partial Classes","Kona November 2023","","",""
3232
"`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|"
33+
"`P2905R2 <https://wg21.link/P2905R2>`__","LWG","Runtime format strings","Kona November 2023","|Complete|","18.0","|format| |DR|"
3434
"`P2918R2 <https://wg21.link/P2918R2>`__","LWG","Runtime format strings II","Kona November 2023","|Complete|","18.0","|format|"
3535
"`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|"
3636
"`P0952R2 <https://wg21.link/P0952R2>`__","LWG","A new specification for ``std::generate_canonical``","Kona November 2023","","",""

libcxx/docs/Status/FormatIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Number,Name,Standard,Assignee,Status,First released version
1717
"`P2510R3 <https://wg21.link/P2510R3>`__","Formatting pointers","C++26","Mark de Wever","|Complete|",17.0
1818
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","C++26","","",
1919
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","C++26","","",
20-
"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|In Progress|"
20+
"`P2905R2 <https://wg21.link/P2905R2>`__","Runtime format strings","C++26 DR","Mark de Wever","|Complete|",18.0
2121
"`P2918R2 <https://wg21.link/P2918R2>`__","Runtime format strings II","C++26","Mark de Wever","|Complete|",18.0
2222
"`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
2323
`P1361 <https://wg21.link/P1361>`_,"Integration of chrono with text formatting","C++20",Mark de Wever,|In Progress|,

libcxx/include/__format/format_arg_store.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,8 +206,8 @@ _LIBCPP_HIDE_FROM_ABI basic_format_arg<_Context> __create_format_arg(_Tp& __valu
206206
}
207207

208208
template <class _Context, class... _Args>
209-
_LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values,
210-
_Args&&... __args) noexcept {
209+
_LIBCPP_HIDE_FROM_ABI void
210+
__create_packed_storage(uint64_t& __types, __basic_format_arg_value<_Context>* __values, _Args&... __args) noexcept {
211211
int __shift = 0;
212212
(
213213
[&] {
@@ -224,7 +224,7 @@ _LIBCPP_HIDE_FROM_ABI void __create_packed_storage(uint64_t& __types, __basic_fo
224224
}
225225

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

libcxx/include/__format/format_functions.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ using wformat_args = basic_format_args<wformat_context>;
6363
#endif
6464

6565
template <class _Context = format_context, class... _Args>
66-
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&&... __args) {
67-
return std::__format_arg_store<_Context, _Args...>(__args...);
66+
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<_Context, _Args...> make_format_args(_Args&... __args) {
67+
return _VSTD::__format_arg_store<_Context, _Args...>(__args...);
6868
}
6969

7070
# ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
7171
template <class... _Args>
7272
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI __format_arg_store<wformat_context, _Args...>
73-
make_wformat_args(_Args&&... __args) {
74-
return std::__format_arg_store<wformat_context, _Args...>(__args...);
73+
make_wformat_args(_Args&... __args) {
74+
return _VSTD::__format_arg_store<wformat_context, _Args...>(__args...);
7575
}
7676
# endif
7777

libcxx/include/format

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,10 @@ namespace std {
177177
178178
template<class Context = format_context, class... Args>
179179
format-arg-store<Context, Args...>
180-
make_format_args(Args&&... args);
180+
make_format_args(Args&... args);
181181
template<class... Args>
182182
format-arg-store<wformat_context, Args...>
183-
make_wformat_args(Args&&... args);
183+
make_wformat_args(Args&... args);
184184
185185
// [format.error], class format_error
186186
class format_error;

libcxx/test/std/input.output/iostream.format/print.fun/no_file_description.pass.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ static void test_vprint_unicode() {
6969
FILE* file = fmemopen(buffer.data(), buffer.size(), "wb");
7070
assert(file);
7171

72-
std::vprint_unicode(file, "hello world{}", std::make_format_args('!'));
72+
char c = '!';
73+
std::vprint_unicode(file, "hello world{}", std::make_format_args(c));
7374
long pos = std::ftell(file);
7475
std::fclose(file);
7576

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

86-
std::vprint_nonunicode(file, "hello world{}", std::make_format_args('!'));
87+
char c = '!';
88+
std::vprint_nonunicode(file, "hello world{}", std::make_format_args(c));
8789
long pos = std::ftell(file);
8890
std::fclose(file);
8991

libcxx/test/std/input.output/iostream.format/print.fun/vprint_nonunicode.sh.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@
3535

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

4145
return 0;
4246
}

libcxx/test/std/input.output/iostream.format/print.fun/vprint_unicode.sh.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,12 @@
3535

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

4145
return 0;
4246
}

libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// <format>
1111

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

1515
#include <cassert>
1616
#include <format>
@@ -20,8 +20,19 @@
2020
#include "test_basic_format_arg.h"
2121
#include "test_macros.h"
2222

23+
template <class... Args>
24+
concept can_make_format_args = requires(Args&&... args) { std::make_format_args(std::forward<Args>(args)...); };
25+
26+
static_assert(can_make_format_args<int&>);
27+
static_assert(!can_make_format_args<int>);
28+
static_assert(!can_make_format_args<int&&>);
29+
2330
int main(int, char**) {
24-
[[maybe_unused]] auto store = std::make_format_args(42, nullptr, false, 'x');
31+
int i = 1;
32+
char c = 'c';
33+
nullptr_t p = nullptr;
34+
bool b = false;
35+
[[maybe_unused]] auto store = std::make_format_args(i, p, b, c);
2536

2637
LIBCPP_STATIC_ASSERT(
2738
std::same_as<decltype(store), std::__format_arg_store<std::format_context, int, nullptr_t, bool, char>>);

libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_format_args.sh.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "test_macros.h"
2525

2626
void test() {
27+
char c = 'c';
2728
TEST_IGNORE_NODISCARD
28-
std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>('c');
29+
std::make_format_args<std::basic_format_context<std::back_insert_iterator<std::basic_string<wchar_t>>, wchar_t>>(c);
2930
}

libcxx/test/std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,27 @@
1212

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

1717
#include <cassert>
1818
#include <format>
1919

2020
#include "test_basic_format_arg.h"
2121
#include "test_macros.h"
2222

23+
template <class... Args>
24+
concept can_make_wformat_args = requires(Args&&... args) { std::make_wformat_args(std::forward<Args>(args)...); };
25+
26+
static_assert(can_make_wformat_args<int&>);
27+
static_assert(!can_make_wformat_args<int>);
28+
static_assert(!can_make_wformat_args<int&&>);
29+
2330
int main(int, char**) {
24-
[[maybe_unused]] auto store = std::make_wformat_args(42, nullptr, false, 'x');
31+
int i = 1;
32+
char c = 'c';
33+
nullptr_t p = nullptr;
34+
bool b = false;
35+
[[maybe_unused]] auto store = std::make_wformat_args(i, p, b, c);
2536

2637
LIBCPP_STATIC_ASSERT(
2738
std::same_as<decltype(store), std::__format_arg_store<std::wformat_context, int, nullptr_t, bool, char>>);

libcxx/test/std/utilities/format/format.arguments/format.args/ctad.compile.pass.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
#include "test_macros.h"
1919

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

2526
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
26-
static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(42))),
27+
static_assert(std::same_as<decltype(std::basic_format_args(std::make_wformat_args(i))),
2728
std::basic_format_args<std::wformat_context>>);
2829

2930
#endif

libcxx/test/std/utilities/format/format.arguments/format.args/ctor.pass.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020

2121
template <class CharT>
2222
void test() {
23+
int i = 1;
24+
char c = 'c';
25+
nullptr_t p = nullptr;
2326
using Context = std::basic_format_context<CharT*, CharT>;
2427
{
2528
ASSERT_NOEXCEPT(std::basic_format_args<Context>{});
@@ -28,22 +31,22 @@ void test() {
2831
assert(!format_args.get(0));
2932
}
3033
{
31-
auto store = std::make_format_args<Context>(1);
34+
auto store = std::make_format_args<Context>(i);
3235
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
3336
std::basic_format_args<Context> format_args{store};
3437
assert(format_args.get(0));
3538
assert(!format_args.get(1));
3639
}
3740
{
38-
auto store = std::make_format_args<Context>(1, 'c');
41+
auto store = std::make_format_args<Context>(i, c);
3942
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
4043
std::basic_format_args<Context> format_args{store};
4144
assert(format_args.get(0));
4245
assert(format_args.get(1));
4346
assert(!format_args.get(2));
4447
}
4548
{
46-
auto store = std::make_format_args<Context>(1, 'c', nullptr);
49+
auto store = std::make_format_args<Context>(i, c, p);
4750
ASSERT_NOEXCEPT(std::basic_format_args<Context>{store});
4851
std::basic_format_args<Context> format_args{store};
4952
assert(format_args.get(0));

libcxx/test/std/utilities/format/format.formatter/format.context/format.context/arg.pass.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323

2424
template <class OutIt, class CharT>
2525
void test() {
26+
bool b = true;
27+
CharT c = CharT('a');
28+
int a = 42;
2629
std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
27-
auto store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(
28-
true, CharT('a'), 42, string);
30+
auto store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
2931
std::basic_format_args args = store;
3032

3133
std::basic_string<CharT> output;

libcxx/test/std/utilities/format/format.formatter/format.context/format.context/ctor.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@
3636

3737
template <class OutIt, class CharT>
3838
void test() {
39+
int a = 42;
40+
bool b = true;
41+
CharT c = CharT('a');
3942
std::basic_string<CharT> string = MAKE_STRING(CharT, "string");
4043
// The type of the object is an exposition only type. The temporary is needed
4144
// to extend the lifetime of the object since args stores a pointer to the
4245
// data in this object.
43-
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
46+
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
4447
std::basic_format_args args = format_arg_store;
4548

4649
{

libcxx/test/std/utilities/format/format.formatter/format.context/format.context/locale.pass.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ void test() {
3434
// The type of the object is an exposition only type. The temporary is needed
3535
// to extend the lifetime of the object since args stores a pointer to the
3636
// data in this object.
37-
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(true, CharT('a'), 42, string);
37+
int a = 42;
38+
bool b = true;
39+
CharT c = CharT('a');
40+
auto format_arg_store = std::make_format_args<std::basic_format_context<OutIt, CharT>>(b, c, a, string);
3841
std::basic_format_args args = format_arg_store;
3942

4043
{

libcxx/test/std/utilities/format/format.formatter/format.formatter.spec/formatter.string.pass.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,7 @@ void test(StringT expected, StringViewT fmt, StringT a, std::size_t offset) {
5353
using FormatCtxT = std::basic_format_context<decltype(out), CharT>;
5454

5555
ArgumentT arg = a;
56-
FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(
57-
out, std::make_format_args<FormatCtxT>(std::forward<ArgumentT>(arg)));
56+
FormatCtxT format_ctx = test_format_context_create<decltype(out), CharT>(out, std::make_format_args<FormatCtxT>(arg));
5857
formatter.format(arg, format_ctx);
5958
assert(result == expected);
6059
}

0 commit comments

Comments
 (0)