From c550bd00e5dd72641def0443beaac86d83da990e Mon Sep 17 00:00:00 2001 From: Alessio <53220763+Atari2@users.noreply.github.com> Date: Fri, 11 Nov 2022 23:46:59 +0100 Subject: [PATCH] Implement P2467R1 `ios_base::noreplace`: Exclusive Mode For `fstream`s (#3065) Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/xiosbase | 3 + stl/inc/yvals_core.h | 2 + stl/src/fiopen.cpp | 25 +++---- tests/std/test.lst | 1 + .../P2467R1_exclusive_mode_fstreams/env.lst | 4 ++ .../P2467R1_exclusive_mode_fstreams/test.cpp | 68 +++++++++++++++++++ .../test.compile.pass.cpp | 14 ++++ 7 files changed, 103 insertions(+), 14 deletions(-) create mode 100644 tests/std/tests/P2467R1_exclusive_mode_fstreams/env.lst create mode 100644 tests/std/tests/P2467R1_exclusive_mode_fstreams/test.cpp diff --git a/stl/inc/xiosbase b/stl/inc/xiosbase index fd4ea7dc14..897ba7162d 100644 --- a/stl/inc/xiosbase +++ b/stl/inc/xiosbase @@ -73,6 +73,9 @@ public: static constexpr _Openmode _Nocreate = static_cast<_Openmode>(0x40); static constexpr _Openmode _Noreplace = static_cast<_Openmode>(0x80); static constexpr _Openmode binary = static_cast<_Openmode>(0x20); +#if _HAS_CXX23 + static constexpr _Openmode noreplace = _Noreplace; +#endif enum _Seekdir { // constants for file positioning options _Seekbeg, diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 38732c242e..626f2424af 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -341,6 +341,7 @@ // P2445R1 forward_like() // P2446R2 views::as_rvalue // P2465R3 Standard Library Modules std And std.compat +// P2467R1 ios_base::noreplace: Exclusive Mode For fstreams // P2494R2 Relaxing Range Adaptors To Allow Move-Only Types // P2499R0 string_view Range Constructor Should Be explicit // P2549R1 unexpected::error() @@ -1661,6 +1662,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_forward_like 202207L #define __cpp_lib_invoke_r 202106L +#define __cpp_lib_ios_noreplace 202207L #define __cpp_lib_is_scoped_enum 202011L #if !defined(__clang__) && !defined(__EDG__) // TRANSITION, Clang and EDG support for modules diff --git a/stl/src/fiopen.cpp b/stl/src/fiopen.cpp index 8c67416b5b..51f8c24cc5 100644 --- a/stl/src/fiopen.cpp +++ b/stl/src/fiopen.cpp @@ -10,14 +10,16 @@ namespace { FILE* _Xfsopen(_In_z_ const char* filename, _In_ int mode, _In_ int prot) { static const char* const mods[] = {// fopen mode strings corresponding to valid[i] - "r", "w", "w", "a", "rb", "wb", "wb", "ab", "r+", "w+", "a+", "r+b", "w+b", "a+b", nullptr}; + "r", "w", "w", "a", "rb", "wb", "wb", "ab", "r+", "w+", "a+", "r+b", "w+b", "a+b", "wx", "wx", "w+x", "wbx", + "wbx", "w+bx", nullptr}; return _fsopen(filename, mods[mode], prot); } FILE* _Xfsopen(_In_z_ const wchar_t* filename, _In_ int mode, _In_ int prot) { static const wchar_t* const mods[] = {// fopen mode strings corresponding to valid[i] - L"r", L"w", L"w", L"a", L"rb", L"wb", L"wb", L"ab", L"r+", L"w+", L"a+", L"r+b", L"w+b", L"a+b", nullptr}; + L"r", L"w", L"w", L"a", L"rb", L"wb", L"wb", L"ab", L"r+", L"w+", L"a+", L"r+b", L"w+b", L"a+b", L"wx", + L"wx", L"w+x", L"wbx", L"wbx", L"w+bx", nullptr}; return _wfsopen(filename, mods[mode], prot); } @@ -40,11 +42,16 @@ namespace { ios_base::in | ios_base::out | ios_base::binary, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary, ios_base::in | ios_base::out | ios_base::app | ios_base::binary, + ios_base::out | ios_base::_Noreplace, + ios_base::out | ios_base::trunc | ios_base::_Noreplace, + ios_base::out | ios_base::in | ios_base::trunc | ios_base::_Noreplace, + ios_base::out | ios_base::binary | ios_base::_Noreplace, + ios_base::out | ios_base::binary | ios_base::trunc | ios_base::_Noreplace, + ios_base::out | ios_base::in | ios_base::trunc | ios_base::binary | ios_base::_Noreplace, }; FILE* fp = nullptr; ios_base::openmode atendflag = mode & ios_base::ate; - ios_base::openmode norepflag = mode & ios_base::_Noreplace; if (mode & ios_base::_Nocreate) { mode |= ios_base::in; // file must exist @@ -54,7 +61,7 @@ namespace { mode |= ios_base::out; // extension -- app implies out } - mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace); + mode &= ~(ios_base::ate | ios_base::_Nocreate); // look for a valid mode int n = 0; @@ -64,16 +71,6 @@ namespace { } } - if (norepflag && (mode & (ios_base::out | ios_base::app)) - && (fp = _Xfsopen(filename, 0, prot)) != nullptr) { // file must not exist, close and fail - fclose(fp); - return nullptr; - } - - if (fp != nullptr && fclose(fp) != 0) { - return nullptr; // can't close after test open - } - if ((fp = _Xfsopen(filename, n, prot)) == nullptr) { return nullptr; // open failed } diff --git a/tests/std/test.lst b/tests/std/test.lst index 65b50c904a..32852bef07 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -574,6 +574,7 @@ tests\P2443R1_views_chunk_by_death tests\P2445R1_forward_like tests\P2446R2_views_as_rvalue tests\P2465R3_standard_library_modules +tests\P2467R1_exclusive_mode_fstreams tests\P2494R2_move_only_range_adaptors tests\P2517R1_apply_conditional_noexcept tests\VSO_0000000_allocator_propagation diff --git a/tests/std/tests/P2467R1_exclusive_mode_fstreams/env.lst b/tests/std/tests/P2467R1_exclusive_mode_fstreams/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P2467R1_exclusive_mode_fstreams/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P2467R1_exclusive_mode_fstreams/test.cpp b/tests/std/tests/P2467R1_exclusive_mode_fstreams/test.cpp new file mode 100644 index 0000000000..183f891152 --- /dev/null +++ b/tests/std/tests/P2467R1_exclusive_mode_fstreams/test.cpp @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +namespace fs = std::filesystem; + +const fs::path test_file{L"test.dat"}; + +struct CreateTempFile { + fs::path m_path; + CreateTempFile(const fs::path& path) : m_path(path) { + std::ofstream{path}; + } + + ~CreateTempFile() { + fs::remove(m_path); + } +}; + +void test_file_create(const std::ios_base::openmode mode) { + { + std::fstream file{test_file, mode}; + assert(!file.fail()); + } + assert(fs::exists(test_file)); + fs::remove(test_file); +} + +void test_file_already_exists(const std::ios_base::openmode mode) { + CreateTempFile tmp{test_file}; + std::fstream file{test_file, mode}; + assert(file.fail()); +} + +void test_file_create_fail(const std::ios_base::openmode bad_mode) { + { + std::fstream file{test_file, bad_mode}; + assert(file.fail()); + } + assert(!fs::exists(test_file)); +} + +int main() { + using IB = std::ios_base; + + test_file_create(IB::out | IB::noreplace); + test_file_create(IB::out | IB::trunc | IB::noreplace); + test_file_create(IB::out | IB::in | IB::trunc | IB::noreplace); + test_file_create(IB::out | IB::binary | IB::noreplace); + test_file_create(IB::out | IB::binary | IB::trunc | IB::noreplace); + test_file_create(IB::out | IB::in | IB::trunc | IB::binary | IB::noreplace); + + test_file_already_exists(IB::out | IB::noreplace); + test_file_already_exists(IB::out | IB::trunc | IB::noreplace); + test_file_already_exists(IB::out | IB::in | IB::trunc | IB::noreplace); + test_file_already_exists(IB::out | IB::binary | IB::noreplace); + test_file_already_exists(IB::out | IB::binary | IB::trunc | IB::noreplace); + test_file_already_exists(IB::out | IB::in | IB::trunc | IB::binary | IB::noreplace); + + test_file_create_fail(IB::in | IB::noreplace); + test_file_create_fail(IB::in | IB::trunc | IB::noreplace); + test_file_create_fail(IB::in | IB::binary | IB::noreplace); + test_file_create_fail(IB::in | IB::binary | IB::trunc | IB::noreplace); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 8c20424224..80265e0975 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1036,6 +1036,20 @@ STATIC_ASSERT(__cpp_lib_invoke_r == 202106L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_ios_noreplace +#error __cpp_lib_ios_noreplace is not defined +#elif __cpp_lib_ios_noreplace != 202207L +#error __cpp_lib_ios_noreplace is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ios_noreplace == 202207L); +#endif +#else +#ifdef __cpp_lib_ios_noreplace +#error __cpp_lib_ios_noreplace is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_is_aggregate #error __cpp_lib_is_aggregate is not defined