Skip to content

Reapply "[libc++][streams] P1759R6: Native handles and file streams" #77190

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_freestanding_variant`` *unimplemented*
--------------------------------------------------- -----------------
``__cpp_lib_fstream_native_handle`` *unimplemented*
``__cpp_lib_fstream_native_handle`` ``202306L``
--------------------------------------------------- -----------------
``__cpp_lib_function_ref`` *unimplemented*
--------------------------------------------------- -----------------
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/18.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Implemented Papers
- P2909R4 - Fix formatting of code units as integers (Dude, where’s my ``char``?)
- P2821R5 - span.at()
- P0521R0 - Proposed Resolution for CA 14 (shared_ptr use_count/unique)
- P1759R6 - Native handles and file streams


Improvements and New Features
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"`P2757R3 <https://wg21.link/P2757R3>`__","LWG","Type-checking format args","Varna June 2023","","","|format|"
"`P2637R3 <https://wg21.link/P2637R3>`__","LWG","Member ``visit``","Varna June 2023","","","|format|"
"`P2641R4 <https://wg21.link/P2641R4>`__","CWG, LWG","Checking if a ``union`` alternative is active","Varna June 2023","","",""
"`P1759R6 <https://wg21.link/P1759R6>`__","LWG","Native handles and file streams","Varna June 2023","","",""
"`P1759R6 <https://wg21.link/P1759R6>`__","LWG","Native handles and file streams","Varna June 2023","|Complete|","18.0",""
"`P2697R1 <https://wg21.link/P2697R1>`__","LWG","Interfacing ``bitset`` with ``string_view``","Varna June 2023","|Complete|","18.0",""
"`P1383R2 <https://wg21.link/P1383R2>`__","LWG","More ``constexpr`` for ``<cmath>`` and ``<complex>``","Varna June 2023","","",""
"`P2734R0 <https://wg21.link/P2734R0>`__","LWG","Adding the new SI prefixes","Varna June 2023","|Complete|","17.0",""
Expand Down
50 changes: 50 additions & 0 deletions libcxx/include/fstream
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
using native_handle_type = typename basic_filebuf<charT, traits>::native_handle_type; // Since C++26

basic_ifstream();
explicit basic_ifstream(const char* s, ios_base::openmode mode = ios_base::in);
Expand All @@ -85,6 +86,7 @@ public:
void swap(basic_ifstream& rhs);

basic_filebuf<char_type, traits_type>* rdbuf() const;
native_handle_type native_handle() const noexcept; // Since C++26
bool is_open() const;
void open(const char* s, ios_base::openmode mode = ios_base::in);
void open(const string& s, ios_base::openmode mode = ios_base::in);
Expand All @@ -110,6 +112,7 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
using native_handle_type = typename basic_filebuf<charT, traits>::native_handle_type; // Since C++26

basic_ofstream();
explicit basic_ofstream(const char* s, ios_base::openmode mode = ios_base::out);
Expand All @@ -122,6 +125,8 @@ public:
void swap(basic_ofstream& rhs);

basic_filebuf<char_type, traits_type>* rdbuf() const;
native_handle_type native_handle() const noexcept; // Since C++26

bool is_open() const;
void open(const char* s, ios_base::openmode mode = ios_base::out);
void open(const string& s, ios_base::openmode mode = ios_base::out);
Expand All @@ -148,6 +153,7 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
using native_handle_type = typename basic_filebuf<charT, traits>::native_handle_type; // Since C++26

basic_fstream();
explicit basic_fstream(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out);
Expand All @@ -160,6 +166,7 @@ public:
void swap(basic_fstream& rhs);

basic_filebuf<char_type, traits_type>* rdbuf() const;
native_handle_type native_handle() const noexcept; // Since C++26
bool is_open() const;
void open(const char* s, ios_base::openmode mode = ios_base::in|ios_base::out);
void open(const string& s, ios_base::openmode mode = ios_base::in|ios_base::out);
Expand Down Expand Up @@ -210,6 +217,10 @@ _LIBCPP_PUSH_MACROS

_LIBCPP_BEGIN_NAMESPACE_STD

# if _LIBCPP_STD_VER >= 26 && defined(_LIBCPP_WIN32API)
_LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept;
# endif

template <class _CharT, class _Traits>
class _LIBCPP_TEMPLATE_VIS basic_filebuf : public basic_streambuf<_CharT, _Traits> {
public:
Expand All @@ -219,6 +230,15 @@ public:
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
typedef typename traits_type::state_type state_type;
# if _LIBCPP_STD_VER >= 26
# if defined(_LIBCPP_WIN32API)
using native_handle_type = void*; // HANDLE
# elif __has_include(<unistd.h>)
using native_handle_type = int; // POSIX file descriptor
# else
# error "Provide a native file handle!"
# endif
# endif

// 27.9.1.2 Constructors/destructor:
basic_filebuf();
Expand All @@ -245,6 +265,18 @@ public:
# endif
_LIBCPP_HIDE_FROM_ABI basic_filebuf* __open(int __fd, ios_base::openmode __mode);
basic_filebuf* close();
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept {
_LIBCPP_ASSERT_UNCATEGORIZED(this->is_open(), "File must be opened");
# if defined(_LIBCPP_WIN32API)
return std::__filebuf_windows_native_handle(__file_);
# elif __has_include(<unistd.h>)
return fileno(__file_);
# else
# error "Provide a way to determine the file native handle!"
# endif
}
# endif // _LIBCPP_STD_VER >= 26

_LIBCPP_HIDE_FROM_ABI inline static const char* __make_mdstring(ios_base::openmode __mode) _NOEXCEPT;

Expand Down Expand Up @@ -1024,6 +1056,9 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
# if _LIBCPP_STD_VER >= 26
using native_handle_type = typename basic_filebuf<_CharT, _Traits>::native_handle_type;
# endif

_LIBCPP_HIDE_FROM_ABI basic_ifstream();
_LIBCPP_HIDE_FROM_ABI explicit basic_ifstream(const char* __s, ios_base::openmode __mode = ios_base::in);
Expand All @@ -1041,6 +1076,9 @@ public:
_LIBCPP_HIDE_FROM_ABI void swap(basic_ifstream& __rhs);

_LIBCPP_HIDE_FROM_ABI basic_filebuf<char_type, traits_type>* rdbuf() const;
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept { return rdbuf()->native_handle(); }
# endif
_LIBCPP_HIDE_FROM_ABI bool is_open() const;
void open(const char* __s, ios_base::openmode __mode = ios_base::in);
# ifdef _LIBCPP_HAS_OPEN_WITH_WCHAR
Expand Down Expand Up @@ -1171,6 +1209,9 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
# if _LIBCPP_STD_VER >= 26
using native_handle_type = typename basic_filebuf<_CharT, _Traits>::native_handle_type;
# endif

_LIBCPP_HIDE_FROM_ABI basic_ofstream();
_LIBCPP_HIDE_FROM_ABI explicit basic_ofstream(const char* __s, ios_base::openmode __mode = ios_base::out);
Expand All @@ -1190,6 +1231,9 @@ public:
_LIBCPP_HIDE_FROM_ABI void swap(basic_ofstream& __rhs);

_LIBCPP_HIDE_FROM_ABI basic_filebuf<char_type, traits_type>* rdbuf() const;
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept { return rdbuf()->native_handle(); }
# endif
_LIBCPP_HIDE_FROM_ABI bool is_open() const;
void open(const char* __s, ios_base::openmode __mode = ios_base::out);
# ifdef _LIBCPP_HAS_OPEN_WITH_WCHAR
Expand Down Expand Up @@ -1321,6 +1365,9 @@ public:
typedef typename traits_type::int_type int_type;
typedef typename traits_type::pos_type pos_type;
typedef typename traits_type::off_type off_type;
# if _LIBCPP_STD_VER >= 26
using native_handle_type = typename basic_filebuf<_CharT, _Traits>::native_handle_type;
# endif

_LIBCPP_HIDE_FROM_ABI basic_fstream();
_LIBCPP_HIDE_FROM_ABI explicit basic_fstream(const char* __s,
Expand All @@ -1345,6 +1392,9 @@ public:
_LIBCPP_HIDE_FROM_ABI void swap(basic_fstream& __rhs);

_LIBCPP_HIDE_FROM_ABI basic_filebuf<char_type, traits_type>* rdbuf() const;
# if _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI native_handle_type native_handle() const noexcept { return rdbuf()->native_handle(); }
# endif
_LIBCPP_HIDE_FROM_ABI bool is_open() const;
_LIBCPP_HIDE_FROM_ABI void open(const char* __s, ios_base::openmode __mode = ios_base::in | ios_base::out);
# ifdef _LIBCPP_HAS_OPEN_WITH_WCHAR
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
// # define __cpp_lib_freestanding_optional 202311L
// # define __cpp_lib_freestanding_string_view 202311L
// # define __cpp_lib_freestanding_variant 202311L
// # define __cpp_lib_fstream_native_handle 202306L
# define __cpp_lib_fstream_native_handle 202306L
// # define __cpp_lib_function_ref 202306L
// # define __cpp_lib_hazard_pointer 202306L
// # define __cpp_lib_linalg 202311L
Expand Down
1 change: 1 addition & 0 deletions libcxx/src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ endif()

if (LIBCXX_ENABLE_LOCALIZATION)
list(APPEND LIBCXX_SOURCES
fstream.cpp
include/sso_allocator.h
ios.cpp
ios.instantiations.cpp
Expand Down
37 changes: 37 additions & 0 deletions libcxx/src/fstream.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <__config>
#include <cstdio>
#include <fstream>

#if defined(_LIBCPP_WIN32API)
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <io.h>
# include <windows.h>
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if defined(_LIBCPP_WIN32API)

// Confirm that `HANDLE` is `void*` as implemented in `basic_filebuf`
static_assert(std::same_as<HANDLE, void*>);

_LIBCPP_EXPORTED_FROM_ABI void* __filebuf_windows_native_handle(FILE* __file) noexcept {
// https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=msvc-170
intptr_t __handle = _get_osfhandle(fileno(__file));
if (__handle == -1)
return nullptr;
return reinterpret_cast<void*>(__handle);
}

#endif

_LIBCPP_END_NAMESPACE_STD
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23

// REQUIRES: has-unix-headers
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
// XFAIL: availability-verbose_abort-missing

// <fstream>

// class basic_filebuf;

// native_handle_type native_handle() const noexcept;

#include <fstream>

#include "../native_handle_assert_test_helpers.h"

int main(int, char**) {
test_native_handle_assertion<std::basic_filebuf<char>>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_native_handle_assertion<std::basic_filebuf<wchar_t>>();
#endif

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23

// <fstream>

// class basic_filebuf;

// native_handle_type native_handle() const noexcept;

#include <cassert>
#include <fstream>
#include <filesystem>
#include <utility>

#include "platform_support.h"
#include "test_macros.h"
#include "../native_handle_test_helpers.h"

template <typename CharT>
void test() {
std::basic_filebuf<CharT> f;
std::filesystem::path p = get_temp_file_name();

// non-const
{
assert(f.open(p, std::ios_base::in) != nullptr);
std::same_as<NativeHandleT> decltype(auto) handle = f.native_handle();
assert(is_handle_valid(handle));
f.close();
assert(!is_handle_valid(handle));
static_assert(noexcept(f.native_handle()));
}
// const
{
assert(f.open(p, std::ios_base::in) != nullptr);
std::same_as<NativeHandleT> decltype(auto) const_handle = std::as_const(f).native_handle();
assert(is_handle_valid(const_handle));
f.close();
assert(!is_handle_valid(const_handle));
static_assert(noexcept(std::as_const(f).native_handle()));
}
}

int main(int, char**) {
test<char>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test<wchar_t>();
#endif

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <type_traits>

#include "test_macros.h"
#include "../native_handle_test_helpers.h"

int main(int, char**)
{
Expand All @@ -32,6 +33,12 @@ int main(int, char**)
static_assert((std::is_same<std::basic_filebuf<char>::int_type, std::char_traits<char>::int_type>::value), "");
static_assert((std::is_same<std::basic_filebuf<char>::pos_type, std::char_traits<char>::pos_type>::value), "");
static_assert((std::is_same<std::basic_filebuf<char>::off_type, std::char_traits<char>::off_type>::value), "");
#if TEST_STD_VER >= 26
test_native_handle_type< std::basic_filebuf<char>>();
# ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_native_handle_type< std::basic_filebuf<wchar_t>>();
# endif
#endif

return 0;
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23

// REQUIRES: has-unix-headers
// REQUIRES: libcpp-hardening-mode={{extensive|debug}}
// XFAIL: availability-verbose_abort-missing

// <fstream>

// class basic_fstream;

// native_handle_type native_handle() const noexcept;

#include <fstream>

#include "../native_handle_assert_test_helpers.h"

int main(int, char**) {
test_native_handle_assertion<std::basic_fstream<char>>();
#ifndef TEST_HAS_NO_WIDE_CHARACTERS
test_native_handle_assertion<std::basic_fstream<wchar_t>>();
#endif

return 0;
}
Loading