[committed] libstdc++: Implement std::ios_base::noreplace for C++23 [PR59769]

Message ID 20211209233022.1569388-1-jwakely@redhat.com
State Committed
Commit a219139e986a4200a9105d7c1fa63735d2945994
Headers
Series [committed] libstdc++: Implement std::ios_base::noreplace for C++23 [PR59769] |

Commit Message

Jonathan Wakely Dec. 9, 2021, 11:30 p.m. UTC
  Tested powerpc64le-linux, pushed to trunk.


This implements my P2467R0 proposal to support opening an fstream in
exclusive mode. The new constant is also supported pre-C++23 as
std::ios_base::__noreplace.

This proposal hasn't been approved for C++23 yet, but I am confident it
will be, as this is restoring a feture found in pre-ISO C++ iostreams
implementations (and still present in the MSVC library as _Noreplace).
If the proposal fails for C++23 we can remove the ios::noreplace
name and just keep ios::__noreplace as an extension.

libstdc++-v3/ChangeLog:

	PR libstdc++/59769
	* config/io/basic_file_stdio.cc (fopen_mode): Add support for
	exclusive mode.
	* include/bits/ios_base.h (_S_noreplace): Define new enumerator.
	(ios_base::__noreplace): Define.
	(ios_base::noreplace): Define for C++23.
	* include/std/version (__cpp_lib_ios_noreplace): Define.
	* testsuite/27_io/basic_ofstream/open/char/noreplace.cc: New test.
	* testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc: New test.
---
 libstdc++-v3/config/io/basic_file_stdio.cc    | 46 +++++++++++--------
 libstdc++-v3/include/bits/ios_base.h          |  9 ++++
 libstdc++-v3/include/std/version              |  1 +
 .../basic_ofstream/open/char/noreplace.cc     | 29 ++++++++++++
 .../basic_ofstream/open/wchar_t/noreplace.cc  | 29 ++++++++++++
 5 files changed, 94 insertions(+), 20 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc
  

Patch

diff --git a/libstdc++-v3/config/io/basic_file_stdio.cc b/libstdc++-v3/config/io/basic_file_stdio.cc
index ce4a6380ebb..06ee016d016 100644
--- a/libstdc++-v3/config/io/basic_file_stdio.cc
+++ b/libstdc++-v3/config/io/basic_file_stdio.cc
@@ -78,32 +78,38 @@  namespace
 	out    = std::ios_base::out,
 	trunc  = std::ios_base::trunc,
 	app    = std::ios_base::app,
-	binary = std::ios_base::binary
+	binary = std::ios_base::binary,
+	noreplace = std::_S_noreplace
       };
 
     // _GLIBCXX_RESOLVE_LIB_DEFECTS
     // 596. 27.8.1.3 Table 112 omits "a+" and "a+b" modes.
-    switch (mode & (in|out|trunc|app|binary))
+    switch (mode & (in|out|trunc|app|binary|noreplace))
       {
-      case (   out                 ): return "w";
-      case (   out      |app       ): return "a";
-      case (             app       ): return "a";
-      case (   out|trunc           ): return "w";
-      case (in                     ): return "r";
-      case (in|out                 ): return "r+";
-      case (in|out|trunc           ): return "w+";
-      case (in|out      |app       ): return "a+";
-      case (in          |app       ): return "a+";
+      case (   out                           ): return "w";
+      case (   out                 |noreplace): return "wx";
+      case (   out|trunc                     ): return "w";
+      case (   out|trunc           |noreplace): return "wx";
+      case (   out      |app                 ): return "a";
+      case (             app                 ): return "a";
+      case (in                               ): return "r";
+      case (in|out                           ): return "r+";
+      case (in|out|trunc                     ): return "w+";
+      case (in|out|trunc           |noreplace): return "w+x";
+      case (in|out      |app                 ): return "a+";
+      case (in          |app                 ): return "a+";
 
-      case (   out          |binary): return "wb";
-      case (   out      |app|binary): return "ab";
-      case (             app|binary): return "ab";
-      case (   out|trunc    |binary): return "wb";
-      case (in              |binary): return "rb";
-      case (in|out          |binary): return "r+b";
-      case (in|out|trunc    |binary): return "w+b";
-      case (in|out      |app|binary): return "a+b";
-      case (in          |app|binary): return "a+b";
+      case (   out          |binary          ): return "wb";
+      case (   out          |binary|noreplace): return "wbx";
+      case (   out      |app|binary          ): return "ab";
+      case (             app|binary          ): return "ab";
+      case (   out|trunc    |binary          ): return "wb";
+      case (in              |binary          ): return "rb";
+      case (in|out          |binary          ): return "r+b";
+      case (in|out|trunc    |binary          ): return "w+b";
+      case (in|out|trunc    |binary|noreplace): return "w+bx";
+      case (in|out      |app|binary          ): return "a+b";
+      case (in          |app|binary          ): return "a+b";
 
       default: return 0; // invalid
       }
diff --git a/libstdc++-v3/include/bits/ios_base.h b/libstdc++-v3/include/bits/ios_base.h
index d6626d4d26b..5db05524e1c 100644
--- a/libstdc++-v3/include/bits/ios_base.h
+++ b/libstdc++-v3/include/bits/ios_base.h
@@ -116,6 +116,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _S_in 		= 1L << 3,
       _S_out 		= 1L << 4,
       _S_trunc 		= 1L << 5,
+      _S_noreplace 	= 1L << 6,
       _S_ios_openmode_end = 1L << 16,
       _S_ios_openmode_max = __INT_MAX__,
       _S_ios_openmode_min = ~__INT_MAX__
@@ -466,6 +467,14 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     /// Truncate an existing stream when opening.  Default for @c ofstream.
     static const openmode trunc =	_S_trunc;
 
+    static const openmode __noreplace =	_S_noreplace;
+
+#if __cplusplus >= 202100L
+#define __cpp_lib_ios_noreplace 202200L
+    /// Open a file in exclusive mode.
+    static const openmode noreplace =	_S_noreplace;
+#endif
+
     // 27.4.2.1.5  Type ios_base::seekdir
     /**
      *  @brief This is an enumerated type.
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index eb612f57c73..23890037b6b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -296,6 +296,7 @@ 
 #define __cpp_lib_adaptor_iterator_pair_constructor 202106L
 #define __cpp_lib_byteswap 202110L
 #define __cpp_lib_invoke_r 202106L
+#define __cpp_lib_ios_noreplace 202200L
 #define __cpp_lib_is_scoped_enum 202011L
 #if __cpp_lib_concepts
 # define __cpp_lib_monadic_optional 202110L
diff --git a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc
new file mode 100644
index 00000000000..e39f5928a1f
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/char/noreplace.cc
@@ -0,0 +1,29 @@ 
+// { dg-do run }
+
+#include <ios>
+
+#if __cplusplus >= 202200L
+#ifndef __cpp_lib_ios_noreplace
+# error "Feature-test macro for ios::noreplace missing in <ios>"
+#elif __cpp_lib_ios_noreplace < 202200L
+# error "Feature-test macro for ios::noreplace has wrong value in <ios>"
+#endif
+#endif
+
+#include <fstream>
+#include <testsuite_hooks.h>
+
+int main()
+{
+#if __cpp_lib_ios_noreplace
+  std::ios::openmode noreplace = std::ios::noreplace;
+#else
+  std::ios::openmode noreplace = std::ios::__noreplace;
+#endif
+
+  std::ofstream of("noreplace");
+  VERIFY( of.is_open() );
+  of.close();
+  of.open("noreplace", noreplace);
+  VERIFY( ! of.is_open() );
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc
new file mode 100644
index 00000000000..77f11865ac4
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_ofstream/open/wchar_t/noreplace.cc
@@ -0,0 +1,29 @@ 
+// { dg-do run }
+
+#include <version>
+
+#if __cplusplus >= 202200L
+#ifndef __cpp_lib_ios_noreplace
+# error "Feature-test macro for ios::noreplace missing in <version>"
+#elif __cpp_lib_ios_noreplace < 202200L
+# error "Feature-test macro for ios::noreplace has wrong value in <version>"
+#endif
+#endif
+
+#include <fstream>
+#include <testsuite_hooks.h>
+
+int main()
+{
+#if __cpp_lib_ios_noreplace
+  std::wios::openmode noreplace = std::wios::noreplace;
+#else
+  std::wios::openmode noreplace = std::wios::__noreplace;
+#endif
+
+  std::wofstream of("noreplace");
+  VERIFY( of.is_open() );
+  of.close();
+  of.open("noreplace", noreplace);
+  VERIFY( ! of.is_open() );
+}