Skip to content

Commit

Permalink
Implement P2467R1 ios_base::noreplace: Exclusive Mode For fstreams (
Browse files Browse the repository at this point in the history
#3065)

Co-authored-by: Casey Carter <[email protected]>
Co-authored-by: Stephan T. Lavavej <[email protected]>
  • Loading branch information
3 people committed Nov 11, 2022
1 parent f58deab commit c550bd0
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 14 deletions.
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
}

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>
#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

0 comments on commit c550bd0

Please sign in to comment.