Skip to content
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

Implement P2467R1 ios_base::noreplace: Exclusive Mode For fstreams (#2932) #3065

Merged
merged 12 commits into from
Nov 11, 2022
Merged
3 changes: 3 additions & 0 deletions stl/inc/xiosbase
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions stl/inc/yvals_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<E>::error()
Expand Down Expand Up @@ -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
Expand Down
25 changes: 11 additions & 14 deletions stl/src/fiopen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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
Expand All @@ -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;
Expand All @@ -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
}
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved

if ((fp = _Xfsopen(filename, n, prot)) == nullptr) {
return nullptr; // open failed
}
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions tests/std/tests/P2467R1_exclusive_mode_fstreams/env.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_latest_matrix.lst
68 changes: 68 additions & 0 deletions tests/std/tests/P2467R1_exclusive_mode_fstreams/test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cassert>
#include <filesystem>
#include <fstream>
StephanTLavavej marked this conversation as resolved.
Show resolved Hide resolved
#include <ios>

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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down