title | document | date | audience | author | toc | toc-depth | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
Native handles and file streams |
P1759R5 |
today |
|
|
true |
2 |
\pagebreak
This paper proposes adding a new typedef to standard file streams: native_handle_type
.
This type is an alias to whatever type the platform uses for its file descriptors:
int
on POSIX, HANDLE
(void*
) on Windows, and something else on other platforms.
This type is a non-owning handle and has generally sane semantics:
default constructability, trivial copyability and it's standard layout.
Alongside this, this paper proposes adding a concrete member function: .native_handle()
,
returning a native_handle_type
, to the following class templates:
basic_filebuf
basic_ifstream
basic_ofstream
basic_fstream
- Remove Motivation, Scope and Prior Art
- Update design discussion
- Add
std::stacktrace_handle
as a type that has a.native_handle()
, and comparisons to it
- Add
- Update wording:
- Update wording to reference [@N4928]
- Expects -> Preconditions
- Other minor changes based on LEWG feedback
- Update wording:
native_handle_type
constexpr default constructor -> default constructor- Add wording as to how the handle returned by
.native_handle()
is valid only when the file is open
- Update wording to reference the latest standard draft [@N4892]
- Add
std::condition_variable
and [@P2146] to list of standard types having a.native_handle()
member function - Update wording to reference the latest standard draft [@N4849], and update references to other P-numbered papers
- Change paper title
- Minor touches to wording
- Refine requirements on
native_handle_type
(removeequality_comparable
, add constexpr default constructability) - Fix some broken references using section numbers in the WD
- Update reference to the WD
- Refine requirements on
- Editorial fixes
- Make
native_handle_type
be standard layout - Add precondition (
is_open() == true
) to.native_handle()
- Add feature test macro
__cpp_lib_fstream_native_handle
- Fix errors with opening the file with POSIX APIs in Motivation (see, we need this paper, fstreams are easier to open correctly!)
- Add additional motivating use case in vectored/scatter-gather IO
Regular
->regular
Incorporate LEWGI feedback from Cologne (July 2019):
- Move to a member function and member typedef
- Make
native_handle
return value not be mandated to be unique - Add note about how the presence of the members is required, and not implementation-defined (like for thread)
Initial revision.
\pagebreak
The wording related to native handles in [thread.req.native]{.sref} is as follows:
Several classes described in this Clause have members
native_handle_type
andnative_handle
. The presence of these members and their semantics is implementation-defined. [ Note: These members allow implementations to provide access to implementation details. Their names are specified to facilitate portable compile-time detection. Actual use of these members is inherently non-portable. — end note ]
In more plain terms, the presence of native handles in std::thread
, std::mutex
, and std::condition_variable
, is implementation-defined,
without a way to query, whether they are provided (except with SFINAE magic).
This design is deemed bad by the author and by LEWG, so this paper aims to not do that again.
The wording related to native handles in [stacktrace.entry.obs]{.sref} is as follows:
The semantics of this function are implementation-defined.
Remarks: Successive invocations of the
native_handle
function for an unchangedstacktrace_entry
object return identical values.
While this design is a huge step up compared to threads, the author would like to have more strict normative guarantees for this facility.
This paper proposes to:
- set requirements for
native_handle_type
:semiregular
ity, trivial copyability, standard-layout - define (semi-normatively), what a native handle for a file means, and how it behaves
This proposal is a pure library extension, requiring no changes to the core language. It would cause no existing conforming code to break.
Implementing this paper should be a relatively trivial task.
Although all implementations surveyed (libstdc++, libc++ and MSVC) use FILE*
instead of native file descriptors in their basic_filebuf
implementations,
these platforms provide facilites to get a native handle from a FILE*
;
fileno
on POSIX, and _fileno
+ _get_osfhandle
on Windows.
The following reference implementations use these.
For libstdc++ on Linux:
template <class CharT, class Traits>
class basic_filebuf : public basic_streambuf<CharT, Traits> {
// ...
using native_handle_type = int;
// ...
native_handle_type native_handle() {
assert(is_open());
// _M_file (__basic_file<char>) has a member function for this purpose
return _M_file.fd();
// ::fileno(_M_file.file()) could also be used
}
// ...
}
For libc++ on Linux:
template <class CharT, class Traits>
class basic_filebuf : public basic_streambuf<CharT, Traits> {
// ...
using native_handle_type = int;
// ...
native_handle_type native_handle() {
assert(is_open());
// __file_ is a FILE*
return ::fileno(__file_)
}
// ...
}
For MSVC:
template <class CharT, class Traits>
class basic_filebuf : public basic_streambuf<CharT, Traits> {
// ...
using native_handle_type = HANDLE;
// ...
native_handle_type native_handle() {
assert(is_open());
// _Myfile is a FILE*
auto cfile = ::_fileno(_Myfile);
// _get_osfhandle returns intptr_t, which can be cast to HANDLE (void*)
return static_cast<HANDLE>(::_get_osfhandle(cfile));
}
// ...
}
For all of these cases, implementing .native_handle()
for ifstream
, ofstream
and fstream
is trivial:
template <class CharT, class Traits>
class basic_ifstream : public basic_istream<CharT, Traits> {
// ...
using native_handle_type =
typename basic_filebuf<CharT, Traits>::native_handle_type;
// ...
native_handle_type native_handle() {
return rdbuf()->native_handle();
}
};
// Repeat for ofstream and fstream
The wording is based on [@N4928].
This paper proposes adding a feature test macro, called __cpp_lib_fstream_native_handle
.
This section is to come between [fstream.syn]{.sref} and [filebuf]{.sref}.
Note to editor: Replace the ? with the appropriate section number. As of [@N4928], that would be 31.10.2.
Also, replace the ? in the Note with the appropriate note number.
::: add
?.?.? Native handles [file.native]
\pnum{1} Several classes described in this section have a member
native_handle_type
.\pnum{2} The type
native_handle_type
represents a platform-specific native handle to a file. It is trivially copyable and standard layout, and modelssemiregular
.\pnum{3} [ Note ?: For operating systems based on POSIX,
native_handle_type
isint
. For Windows-based operating systems,native_handle_type
isHANDLE
. — end note ]
:::
namespace std {
template<class charT, class traits = char_traits<charT>>
class basic_filebuf : public basic_streambuf<charT, traits> {
public:
using char_type = charT;
using int_type = typename traits::int_type;
using pos_type = typename traits::pos_type;
using off_type = typename traits::off_type;
using traits_type = traits;
+ using native_handle_type = @_implementation-defined_@; // see [file.native]
// ...
// [filebuf.members], members
bool is_open() const;
basic_filebuf* open(const char* s, ios_base::openmode mode);
basic_filebuf* open(const filesystem::path::value_type* s,
ios_base::openmode mode); // wide systems only; see 31.10.1
basic_filebuf* open(const string& s,
ios_base::openmode mode);
basic_filebuf* open(const filesystem::path& s,
ios_base::openmode mode);
basic_filebuf* close();
+ native_handle_type native_handle();
// ...
}
}
Note to editor: Replace the ? in the note number below with the appropriate note number.
\pnum{4} An instance of
basic_filebuf
behaves as described in [filebuf] providedtraits::pos_type
isfpos<traits::state_type>
. Otherwise the behavior is undefined.::: add \pnum{5} The underlying file of a
basic_filebuf
has an associated value of typenative_handle_type
, called the native handle of that file. This native handle can be obtained with the member functionnative_handle
. Whether a value ofnative_handle_type
not obtained by callingnative_handle()
may be a valid native handle is implementation-defined. Whether the associated native handle is unique for each file, is implementation-defined.\pnum{6} For any opened
basic_filebuf
f
, the native handle returned byf.native_handle()
is invalidated whenf.close()
is called, orf
is destroyed.\pnum{7} [ Note ?: The type
native_handle_type
, and the member functionnative_handle
, of file-based streams are always defined. This is consistent with native handles ofstacktrace_entry
[stacktrace.entry], but differs fromthread
,mutex
andcondition_variable
[thread.req.native], the presence of which is implementation-defined. — end note ] :::\pnum{8} In order to support file I/O and multibyte/wide character conversion, conversions are performed using members of a facet, referred to as
a_codecvt
in the following subclauses, obtained as if by...
This would come after the definition of basic_filebuf::close()
, which occupies paragraphs 8-10.
::: add
native_handle_type native_handle();
\pnum{11} Preconditions:
is_open()
istrue
.\pnum{12} Throws: Nothing.
\pnum{13} Returns: The native handle associated with this file.
:::
namespace std {
template<class charT, class traits = char_traits<charT>>
class basic_ifstream : public basic_istream<charT, traits> {
public:
using char_type = charT;
using int_type = typename traits::int_type;
using pos_type = typename traits::pos_type;
using off_type = typename traits::off_type;
using traits_type = traits;
+ using native_handle_type =
+ typename basic_filebuf<charT, traits>::native_handle_type;
// ...
// [ifstream.members], members
basic_filebuf<charT, traits>* rdbuf() const;
+ native_handle_type native_handle();
bool is_open() const;
// ...
}
}
This would come between the definitions of basic_ifstream::rdbuf()
(p1) and basic_ifstream::is_open()
(p2, now p3).
::: add
native_handle_type native_handle();
\pnum{2} Effects: Equivalent to:
return rdbuf()->native_handle();
.
:::
namespace std {
template<class charT, class traits = char_traits<charT>>
class basic_ofstream : public basic_ostream<charT, traits> {
public:
using char_type = charT;
using int_type = typename traits::int_type;
using pos_type = typename traits::pos_type;
using off_type = typename traits::off_type;
using traits_type = traits;
+ using native_handle_type =
+ typename basic_filebuf<charT, traits>::native_handle_type;
// ...
// [ofstream.members], members
basic_filebuf<charT, traits>* rdbuf() const;
+ native_handle_type native_handle();
bool is_open() const;
// ...
}
}
This would come between the definitions of basic_ofstream::rdbuf()
(p1) and basic_ofstream::is_open()
(p2, now p3).
::: add
native_handle_type native_handle();
\pnum{2} Effects: Equivalent to:
return rdbuf()->native_handle();
.
:::
namespace std {
template<class charT, class traits = char_traits<charT>>
class basic_fstream : public basic_iostream<charT, traits> {
public:
using char_type = charT;
using int_type = typename traits::int_type;
using pos_type = typename traits::pos_type;
using off_type = typename traits::off_type;
using traits_type = traits;
+ using native_handle_type =
+ typename basic_filebuf<charT, traits>::native_handle_type;
// ...
// [fstream.members], members
basic_filebuf<charT, traits>* rdbuf() const;
+ native_handle_type native_handle();
+
bool is_open() const;
// ...
}
}
This would come between the definitions of basic_fstream::rdbuf()
(p1) and basic_fstream::is_open()
(p2, now p3).
::: add
native_handle_type native_handle();
\pnum{2} Effects: Equivalent to:
return rdbuf()->native_handle();
.
:::
\pagebreak
Thanks to Jonathan Wakely for reviewing the wording for R3 of this paper.
Thanks to Niall Douglas for feedback, encouragement and ambitious suggestions for this paper.
Thanks to the rest of the co-authors of [@P1750] for the idea after cutting this functionality out, especially to Jeff Garland for providing a heads-up about a possible ABI-break that I totally would've missed, even though it ended up being a non-issue.
Thanks to Michael Park for his paper markup framework [@mpark-wg21].
references:
- id: N4734
citation-label: N4734
title: "Working Draft, C++ Extensions for Networking"
issued:
year: 2018
URL: https://wg21.link/N4734
author:
- family: Wakely given: Jonathan
- id: N4928
citation-label: N4928
title: "Working Draft, Standard for Programming Language C++"
issued:
year: 2022
URL: https://wg21.link/N4928
author:
- family: Köppe given: Thomas
- id: N4892
citation-label: N4892
title: "Working Draft, Standard for Programming Language C++"
issued:
year: 2022
URL: https://wg21.link/N4892
author:
- family: Smith given: Richard
- id: P1750
citation-label: P1750R1
title: "A Proposal to Add Process Management to the C++ Standard Library"
URL: "https://wg21.link/p1750r1"
issued:
year: 2019
author:
- family: Morgernstern given: Klemens
- family: Garland given: Jeff
- family: Kosunen given: Elias
- family: Bakir given: Fatih
- id: P1031
citation-label: P1031R2
title: "Low level file i/o library"
URL: "https://wg21.link/p1031r2"
issued:
year: 2019
author:
- family: Douglas given: Niall
- id: P2146
citation-label: P2146R2
title: "Modern std::byte stream IO for C++"
URL: "https://wg21.link/p2146r2"
issued:
year: 2020
author:
- family: Kornoushenko given: Amanda
- id: mpark-wg21 citation-label: mpark/wg21 title: "mpark/wg21 on GitHub" URL: "https://github.com/mpark/wg21"
- id: std-proposals-native-handle
citation-label: std-proposals-native-handle
title: "
native_handle
forbasic_filebuf
– std-proposals" URL: "https://groups.google.com/a/isocpp.org/d/topic/std-proposals/oCEErQbI9sM/discussion" - id: std-discussion-fd-io citation-label: std-discussion-fd-io title: "File descriptor-backed I/O stream? – std-discussion" URL: "https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/macDvhFDrjU"
- id: std-proposals-native-raw-io
citation-label: std-proposals-native-raw-io
title: "Native raw IO and
FILE*
wrappers? – std-proposals" URL: "https://groups.google.com/a/isocpp.org/d/topic/std-proposals/Q4RdFSZggSE/discussion" - id: std-proposals-fd-access citation-label: std-proposals-fd-access title: "file streams and access to the file descriptor – std-proposals" URL: "https://groups.google.com/a/isocpp.org/d/topic/std-proposals/XcQ4FZJKDbM/discussion"
- id: access-file-descriptors
citation-label: access-file-descriptors
title: "file streams and access to the file descriptor"
URL: "https://docs.google.com/viewer?a=v&pid=forums&srcid=MTEwODAzNzI2MjM1OTc0MjE3MjkBMDY0OTY1OTUzMjAwNzY0MTA0MjkBakhWMHBFLUNGd0FKATAuMQFpc29jcHAub3JnAXYy&authuser=0"
author:
- family: Adams given: [Bruce, S., O.]
- id: Boost.IOStreams
citation-label: Boost.IOStreams
title: "Boost.IOStreams"
URL: "https://www.boost.org/doc/libs/1_71_0/libs/iostreams/doc/index.html"
author:
- family: Turkanis given: Jonathan