Skip to content

Commit a3f17ba

Browse files
PragmaTwiceldionne
andcommitted
[libc++] Implement P2467R1: Support exclusive mode for fstreams
This patch brings std::ios_base::noreplace from P2467R1 to libc++. This requires compiling the shared library in C++23 mode since otherwise fstream::open(...) doesn't know about the new flag. Differential Revision: https://reviews.llvm.org/D137640 Co-authored-by: Louis Dionne <[email protected]>
1 parent 341ca1a commit a3f17ba

File tree

14 files changed

+416
-12
lines changed

14 files changed

+416
-12
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,8 @@ Status
332332
--------------------------------------------------- -----------------
333333
``__cpp_lib_invoke_r`` ``202106L``
334334
--------------------------------------------------- -----------------
335+
``__cpp_lib_ios_noreplace`` ``202207L``
336+
--------------------------------------------------- -----------------
335337
``__cpp_lib_is_scoped_enum`` ``202011L``
336338
--------------------------------------------------- -----------------
337339
``__cpp_lib_mdspan`` ``202207L``

libcxx/docs/ReleaseNotes/18.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Implemented Papers
4848
- P2538R1 - ADL-proof ``std::projected``
4949
- P2614R2 - Deprecate ``numeric_limits::has_denorm``
5050
- P0053R7 - C++ Synchronized Buffered Ostream (in the experimental library)
51+
- P2467R1 - Support exclusive mode for fstreams
5152

5253

5354
Improvements and New Features

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
"`P2446R2 <https://wg21.link/P2446R2>`__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0","|ranges|"
7777
"`P2460R2 <https://wg21.link/P2460R2>`__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","",""
7878
"`P2465R3 <https://wg21.link/P2465R3>`__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","",""
79-
"`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","",""
79+
"`P2467R1 <https://wg21.link/P2467R1>`__","LWG","Support exclusive mode for ``fstreams``","July 2022","|Complete|","18.0",""
8080
"`P2474R2 <https://wg21.link/P2474R2>`__","LWG","``views::repeat``","July 2022","|Complete|","17.0","|ranges|"
8181
"`P2494R2 <https://wg21.link/P2494R2>`__","LWG","Relaxing range adaptors to allow for move only types","July 2022","|Complete|","17.0","|ranges|"
8282
"`P2499R0 <https://wg21.link/P2499R0>`__","LWG","``string_view`` range constructor should be ``explicit``","July 2022","|Complete|","16.0","|ranges|"

libcxx/include/fstream

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,18 @@ const char* basic_filebuf<_CharT, _Traits>::__make_mdstring(
552552
case ios_base::in | ios_base::out | ios_base::app | ios_base::binary:
553553
case ios_base::in | ios_base::app | ios_base::binary:
554554
return "a+b" _LIBCPP_FOPEN_CLOEXEC_MODE;
555+
#if _LIBCPP_STD_VER >= 23
556+
case ios_base::out | ios_base::noreplace:
557+
case ios_base::out | ios_base::trunc | ios_base::noreplace:
558+
return "wx" _LIBCPP_FOPEN_CLOEXEC_MODE;
559+
case ios_base::in | ios_base::out | ios_base::trunc | ios_base::noreplace:
560+
return "w+x" _LIBCPP_FOPEN_CLOEXEC_MODE;
561+
case ios_base::out | ios_base::binary | ios_base::noreplace:
562+
case ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
563+
return "wbx" _LIBCPP_FOPEN_CLOEXEC_MODE;
564+
case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
565+
return "w+bx" _LIBCPP_FOPEN_CLOEXEC_MODE;
566+
#endif // _LIBCPP_STD_VER >= 23
555567
default:
556568
return nullptr;
557569
}
@@ -665,6 +677,22 @@ basic_filebuf<_CharT, _Traits>::open(const wchar_t* __s, ios_base::openmode __mo
665677
case ios_base::in | ios_base::app | ios_base::binary:
666678
__mdstr = L"a+b";
667679
break;
680+
# if _LIBCPP_STD_VER >= 23
681+
case ios_base::out | ios_base::noreplace:
682+
case ios_base::out | ios_base::trunc | ios_base::noreplace:
683+
__mdstr = L"wx";
684+
break;
685+
case ios_base::in | ios_base::out | ios_base::trunc | ios_base::noreplace:
686+
__mdstr = L"w+x";
687+
break;
688+
case ios_base::out | ios_base::binary | ios_base::noreplace:
689+
case ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
690+
__mdstr = L"wbx";
691+
break;
692+
case ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary | ios_base::noreplace:
693+
__mdstr = L"w+bx";
694+
break;
695+
# endif // _LIBCPP_STD_VER >= 23
668696
default:
669697
__rt = nullptr;
670698
break;

libcxx/include/ios

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public:
5858
static constexpr openmode ate;
5959
static constexpr openmode binary;
6060
static constexpr openmode in;
61+
static constexpr openmode noreplace; // since C++23
6162
static constexpr openmode out;
6263
static constexpr openmode trunc;
6364
@@ -277,12 +278,15 @@ public:
277278
static const iostate goodbit = 0x0;
278279

279280
typedef unsigned int openmode;
280-
static const openmode app = 0x01;
281-
static const openmode ate = 0x02;
282-
static const openmode binary = 0x04;
283-
static const openmode in = 0x08;
284-
static const openmode out = 0x10;
285-
static const openmode trunc = 0x20;
281+
static const openmode app = 0x01;
282+
static const openmode ate = 0x02;
283+
static const openmode binary = 0x04;
284+
static const openmode in = 0x08;
285+
static const openmode out = 0x10;
286+
static const openmode trunc = 0x20;
287+
#if _LIBCPP_STD_VER >= 23
288+
static const openmode noreplace = 0x40;
289+
#endif
286290

287291
enum seekdir {beg, cur, end};
288292

libcxx/include/version

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ __cpp_lib_integral_constant_callable 201304L <type_traits>
121121
__cpp_lib_interpolate 201902L <cmath> <numeric>
122122
__cpp_lib_invoke 201411L <functional>
123123
__cpp_lib_invoke_r 202106L <functional>
124+
__cpp_lib_ios_noreplace 202207L <ios>
124125
__cpp_lib_is_aggregate 201703L <type_traits>
125126
__cpp_lib_is_constant_evaluated 201811L <type_traits>
126127
__cpp_lib_is_final 201402L <type_traits>
@@ -447,6 +448,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
447448
// # define __cpp_lib_formatters 202302L
448449
# define __cpp_lib_forward_like 202207L
449450
# define __cpp_lib_invoke_r 202106L
451+
# define __cpp_lib_ios_noreplace 202207L
450452
# define __cpp_lib_is_scoped_enum 202011L
451453
# define __cpp_lib_mdspan 202207L
452454
// # define __cpp_lib_move_only_function 202110L

libcxx/test/std/input.output/file.streams/fstreams/filebuf.members/open_pointer.pass.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
// basic_filebuf<charT,traits>* open(const char* s, ios_base::openmode mode);
1212

13+
// XFAIL: LIBCXX-AIX-FIXME
14+
1315
#include <fstream>
1416
#include <cassert>
1517
#include "test_macros.h"
@@ -52,5 +54,57 @@ int main(int, char**)
5254
std::remove(temp.c_str());
5355
#endif
5456

55-
return 0;
57+
#if TEST_STD_VER >= 23
58+
// Test all the noreplace flag combinations
59+
{
60+
std::ios_base::openmode modes[] = {
61+
std::ios_base::out | std::ios_base::noreplace,
62+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
63+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
64+
std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary,
65+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary,
66+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace |
67+
std::ios_base::binary,
68+
};
69+
for (auto mode : modes) {
70+
std::string tmp = get_temp_file_name(); // also creates the file
71+
72+
{
73+
std::filebuf f;
74+
f.open(tmp.c_str(), mode);
75+
assert(!f.is_open()); // since it already exists
76+
}
77+
78+
{
79+
std::remove(tmp.c_str());
80+
81+
std::filebuf f;
82+
f.open(tmp.c_str(), mode);
83+
assert(f.is_open()); // since it doesn't exist
84+
}
85+
}
86+
87+
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
88+
for (auto mode : modes) {
89+
std::string tmp = get_temp_file_name(); // also creates the file
90+
91+
{
92+
std::wfilebuf f;
93+
f.open(tmp.c_str(), mode);
94+
assert(!f.is_open()); // since it already exists
95+
}
96+
97+
{
98+
std::remove(tmp.c_str());
99+
100+
std::wfilebuf f;
101+
f.open(tmp.c_str(), mode);
102+
assert(f.is_open()); // since it doesn't exist
103+
}
104+
}
105+
# endif
106+
}
107+
#endif // TEST_STD_VER >= 23
108+
109+
return 0;
56110
}

libcxx/test/std/input.output/file.streams/fstreams/fstream.cons/pointer.pass.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
// explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in | ios_base::out);
1515

16+
// XFAIL: LIBCXX-AIX-FIXME
17+
1618
#include <fstream>
1719
#include <cassert>
1820
#include "test_macros.h"
@@ -45,5 +47,53 @@ int main(int, char**)
4547
std::remove(temp.c_str());
4648
#endif
4749

48-
return 0;
50+
#if TEST_STD_VER >= 23
51+
// Test all the noreplace flag combinations
52+
{
53+
std::ios_base::openmode modes[] = {
54+
std::ios_base::out | std::ios_base::noreplace,
55+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
56+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
57+
std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary,
58+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary,
59+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace |
60+
std::ios_base::binary,
61+
};
62+
for (auto mode : modes) {
63+
std::string tmp = get_temp_file_name(); // also creates the file
64+
65+
{
66+
std::fstream f(tmp.c_str(), mode);
67+
assert(!f.is_open()); // since it already exists
68+
}
69+
70+
{
71+
std::remove(tmp.c_str());
72+
73+
std::fstream f(tmp.c_str(), mode);
74+
assert(f.is_open()); // since it doesn't exist
75+
}
76+
}
77+
78+
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
79+
for (auto mode : modes) {
80+
std::string tmp = get_temp_file_name(); // also creates the file
81+
82+
{
83+
std::wfstream f(tmp.c_str(), mode);
84+
assert(!f.is_open()); // since it already exists
85+
}
86+
87+
{
88+
std::remove(tmp.c_str());
89+
90+
std::wfstream f(tmp.c_str(), mode);
91+
assert(f.is_open()); // since it doesn't exist
92+
}
93+
}
94+
# endif
95+
}
96+
#endif // TEST_STD_VER >= 23
97+
98+
return 0;
4999
}

libcxx/test/std/input.output/file.streams/fstreams/fstream.members/open_pointer.pass.cpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
// void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out);
1515

16+
// XFAIL: LIBCXX-AIX-FIXME
17+
1618
#include <fstream>
1719
#include <cassert>
1820
#include "test_macros.h"
@@ -51,5 +53,57 @@ int main(int, char**)
5153
std::remove(temp.c_str());
5254
#endif
5355

54-
return 0;
56+
#if TEST_STD_VER >= 23
57+
// Test all the noreplace flag combinations
58+
{
59+
std::ios_base::openmode modes[] = {
60+
std::ios_base::out | std::ios_base::noreplace,
61+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
62+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
63+
std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary,
64+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary,
65+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace |
66+
std::ios_base::binary,
67+
};
68+
for (auto mode : modes) {
69+
std::string tmp = get_temp_file_name(); // also creates the file
70+
71+
{
72+
std::fstream f;
73+
f.open(tmp.c_str(), mode);
74+
assert(!f.is_open()); // since it already exists
75+
}
76+
77+
{
78+
std::remove(tmp.c_str());
79+
80+
std::fstream f;
81+
f.open(tmp.c_str(), mode);
82+
assert(f.is_open()); // since it doesn't exist
83+
}
84+
}
85+
86+
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
87+
for (auto mode : modes) {
88+
std::string tmp = get_temp_file_name(); // also creates the file
89+
90+
{
91+
std::wfstream f;
92+
f.open(tmp.c_str(), mode);
93+
assert(!f.is_open()); // since it already exists
94+
}
95+
96+
{
97+
std::remove(tmp.c_str());
98+
99+
std::wfstream f;
100+
f.open(tmp.c_str(), mode);
101+
assert(f.is_open()); // since it doesn't exist
102+
}
103+
}
104+
# endif
105+
}
106+
#endif // TEST_STD_VER >= 23
107+
108+
return 0;
55109
}

libcxx/test/std/input.output/file.streams/fstreams/ofstream.cons/pointer.pass.cpp

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
// explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out);
1515

16+
// XFAIL: LIBCXX-AIX-FIXME
17+
1618
#include <fstream>
1719
#include <cassert>
1820
#include "test_macros.h"
@@ -59,5 +61,53 @@ int main(int, char**)
5961
std::remove(temp.c_str());
6062
#endif
6163

62-
return 0;
64+
#if TEST_STD_VER >= 23
65+
// Test all the noreplace flag combinations
66+
{
67+
std::ios_base::openmode modes[] = {
68+
std::ios_base::out | std::ios_base::noreplace,
69+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
70+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace,
71+
std::ios_base::out | std::ios_base::noreplace | std::ios_base::binary,
72+
std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace | std::ios_base::binary,
73+
std::ios_base::in | std::ios_base::out | std::ios_base::trunc | std::ios_base::noreplace |
74+
std::ios_base::binary,
75+
};
76+
for (auto mode : modes) {
77+
std::string tmp = get_temp_file_name(); // also creates the file
78+
79+
{
80+
std::ofstream f(tmp.c_str(), mode);
81+
assert(!f.is_open()); // since it already exists
82+
}
83+
84+
{
85+
std::remove(tmp.c_str());
86+
87+
std::ofstream f(tmp.c_str(), mode);
88+
assert(f.is_open()); // since it doesn't exist
89+
}
90+
}
91+
92+
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
93+
for (auto mode : modes) {
94+
std::string tmp = get_temp_file_name(); // also creates the file
95+
96+
{
97+
std::wofstream f(tmp.c_str(), mode);
98+
assert(!f.is_open()); // since it already exists
99+
}
100+
101+
{
102+
std::remove(tmp.c_str());
103+
104+
std::wofstream f(tmp.c_str(), mode);
105+
assert(f.is_open()); // since it doesn't exist
106+
}
107+
}
108+
# endif
109+
}
110+
#endif // TEST_STD_VER >= 23
111+
112+
return 0;
63113
}

0 commit comments

Comments
 (0)