Skip to content

Commit 462aeec

Browse files
authored
Implement file::native_handle for C++17 (#172)
When opening `tmp::filebuf`, simply save the open handle, which can be used to get a native handle
1 parent e877773 commit 462aeec

File tree

6 files changed

+71
-22
lines changed

6 files changed

+71
-22
lines changed

include/tmp/file

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,12 @@ public:
6161
static file copy(const std::filesystem::path& path,
6262
std::ios::openmode mode = std::ios::in | std::ios::out);
6363

64-
#if __cpp_lib_fstream_native_handle >= 202306L
6564
/// Implementation-defined handle type to the temporary file
66-
using native_handle_type = std::filebuf::native_handle_type;
65+
using native_handle_type = filebuf::native_handle_type;
6766

68-
/// Returns an implementation-defined handle to this entry
67+
/// Returns an implementation-defined handle to this file
6968
/// @returns The underlying implementation-defined handle
70-
native_handle_type native_handle() const noexcept {
71-
return rdbuf()->native_handle();
72-
}
73-
#endif
69+
native_handle_type native_handle() const noexcept;
7470

7571
/// Returns pointer to the underlying raw file device object
7672
/// @returns A pointer to the underlying raw file device

include/tmp/filebuf

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,40 @@ namespace tmp {
1616
/// opening a file fails
1717
class filebuf : public std::filebuf {
1818
public:
19+
/// Implementation-defined handle type to the open file
20+
#if defined(_WIN32)
21+
using open_handle_type = std::FILE*;
22+
#elif __has_include(<unistd.h>)
23+
using open_handle_type = int; // POSIX file descriptor
24+
#else
25+
#error "Target platform not supported"
26+
#endif
27+
1928
/// Implementation-defined handle type to the file
2029
#if defined(_WIN32)
21-
using native_handle_type = std::FILE*;
30+
using native_handle_type = void*; // HANDLE
2231
#elif __has_include(<unistd.h>)
2332
using native_handle_type = int; // POSIX file descriptor
2433
#else
2534
#error "Target platform not supported"
2635
#endif
2736

37+
/// Creates a file buffer that is not associated with any file
38+
filebuf() noexcept;
39+
2840
/// Opens a file and configures it as the associated character sequence
2941
/// @param handle A handle to the open file
3042
/// @param mode The file opening mode
3143
/// @returns `this` file buffer, or a null pointer on failure
32-
filebuf* open(native_handle_type handle, std::ios::openmode mode);
44+
filebuf* open(open_handle_type handle, std::ios::openmode mode);
45+
46+
/// Returns an implementation-defined handle to this file
47+
/// @returns The underlying implementation-defined handle
48+
native_handle_type native_handle() const noexcept;
49+
50+
private:
51+
/// Native handle to the open file
52+
open_handle_type handle;
3353
};
3454
} // namespace tmp
3555

src/create.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ const wchar_t* make_mdstring(std::ios::openmode mode) noexcept {
119119

120120
/// Closes the given handle, ignoring any errors
121121
/// @param[in] handle The handle to close
122-
void close(filebuf::native_handle_type handle) noexcept {
122+
void close(filebuf::open_handle_type handle) noexcept {
123123
#ifdef _WIN32
124124
fclose(handle);
125125
#else

src/file.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ file file::copy(const fs::path& path, std::ios::openmode mode) {
3535
return tmpfile;
3636
}
3737

38+
file::native_handle_type file::native_handle() const noexcept {
39+
return sb.native_handle();
40+
}
41+
3842
std::filebuf* file::rdbuf() const noexcept {
3943
return const_cast<filebuf*>(std::addressof(sb)); // NOLINT(*-const-cast)
4044
}

src/filebuf.cpp

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,40 @@
22

33
#include <ios>
44

5+
#ifdef _WIN32
6+
#include <Windows.h>
7+
#include <io.h>
8+
#endif
9+
510
#if __has_include(<__config>)
611
#include <__config> // libc++ configuration
712
#endif
813

914
namespace tmp {
1015

11-
filebuf* filebuf::open(native_handle_type handle, std::ios::openmode mode) {
16+
// Confirm that native_handle_type matches `TriviallyCopyable` named requirement
17+
static_assert(std::is_trivially_copyable_v<filebuf::native_handle_type>);
18+
19+
#ifdef _WIN32
20+
// Confirm that `HANDLE` is as implemented in `entry`
21+
static_assert(std::is_same_v<HANDLE, filebuf::native_handle_type>);
22+
#endif
23+
24+
filebuf::filebuf() noexcept
25+
:
26+
#if defined(_LIBCPP_VERSION) || defined(__GLIBCXX__)
27+
handle(-1)
28+
#elif defined(_MSC_VER)
29+
handle(nullptr)
30+
#elif
31+
#error "Target C++ standard library is not supported"
32+
#endif
33+
{
34+
}
35+
36+
filebuf* filebuf::open(open_handle_type handle, std::ios::openmode mode) {
37+
this->handle = handle;
38+
1239
#if defined(_LIBCPP_VERSION)
1340
return this->__open(handle, mode) != nullptr ? this : nullptr;
1441
#elif defined(__GLIBCXX__)
@@ -40,4 +67,17 @@ filebuf* filebuf::open(native_handle_type handle, std::ios::openmode mode) {
4067
#error "Target C++ standard library is not supported"
4168
#endif
4269
}
70+
71+
filebuf::native_handle_type filebuf::native_handle() const noexcept {
72+
#ifdef _WIN32
73+
intptr_t osfhandle = _get_osfhandle(_fileno(handle));
74+
if (osfhandle == -1) {
75+
return nullptr;
76+
}
77+
78+
return reinterpret_cast<void*>(osfhandle);
79+
#else
80+
return handle;
81+
#endif
82+
}
4383
} // namespace tmp

tests/file.cpp

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ bool is_open(const file& file) {
2626
return file.rdbuf()->is_open();
2727
}
2828

29-
#if __cpp_lib_fstream_native_handle >= 202306L
3029
/// Checks if the given file handle is valid
3130
/// @param handle handle to check
3231
/// @returns whether the handle is valid
@@ -38,7 +37,6 @@ bool is_open(file::native_handle_type handle) {
3837
return fcntl(handle, F_GETFD) != -1;
3938
#endif
4039
}
41-
#endif
4240

4341
/// Tests file type traits and member types
4442
TEST(file, type_traits) {
@@ -61,10 +59,7 @@ TEST(file, create) {
6159
EXPECT_TRUE(fs::is_regular_file(tmpfile));
6260
EXPECT_TRUE(fs::equivalent(parent, fs::temp_directory_path()));
6361
EXPECT_TRUE(is_open(tmpfile));
64-
65-
#if __cpp_lib_fstream_native_handle >= 202306L
6662
EXPECT_TRUE(is_open(tmpfile.native_handle()));
67-
#endif
6863

6964
fs::perms permissions = fs::status(tmpfile).permissions();
7065
#ifdef _WIN32
@@ -199,22 +194,16 @@ TEST(file, move_to_non_existing_directory) {
199194
/// Tests that destructor removes a file
200195
TEST(file, destructor) {
201196
fs::path path;
202-
#if __cpp_lib_fstream_native_handle >= 202306L
203197
file::native_handle_type handle;
204-
#endif
205198

206199
{
207200
file tmpfile = file();
208201
path = tmpfile;
209-
#if __cpp_lib_fstream_native_handle >= 202306L
210202
handle = tmpfile.native_handle();
211-
#endif
212203
}
213204

214205
EXPECT_FALSE(fs::exists(path));
215-
#if __cpp_lib_fstream_native_handle >= 202306L
216206
EXPECT_FALSE(is_open(handle));
217-
#endif
218207
}
219208

220209
/// Tests file move constructor

0 commit comments

Comments
 (0)