The main posixpp library and associated tests.
Revision | 845b89885277181b72a9841b917c8bb7a4789200 (tree) |
---|---|
Time | 2021-05-03 15:30:36 |
Author | Eric Hopper <hopper@omni...> |
Commiter | Eric Hopper |
Make read, write, and open be 'free' functions. Justification...
These are not intrinsic to every kind of file descriptor. If open is a
member, so should epoll_create, socket, and signalfd (among others). But
that would require dragging in a whole bunch of constants and types
associated solely with each of these calls. That doesn't seem right, the
fd type should be thin.
So, the fd type will now contain only a very few functions that are relevant
for just about any kind of file descriptor.
Of course, the magic of argument dependent lookup enables some of these now
'free' functions (like read and write) to be called without referencing the
namespace they're in because C++ rightly considers them part of the
interface for the fd type they take.
@@ -10,7 +10,7 @@ | ||
10 | 10 | |
11 | 11 | find_package(fmt REQUIRED) |
12 | 12 | |
13 | -add_library(posixpp SHARED empty.cpp) | |
13 | +add_library(posixpp SHARED empty.cpp pubincludes/posixpp/simpleio.h) | |
14 | 14 | set_property(TARGET posixpp PROPERTY CXX_EXTENSIONS OFF) |
15 | 15 | target_compile_features(posixpp PUBLIC cxx_std_20) |
16 | 16 | target_include_directories(posixpp PUBLIC |
@@ -18,7 +18,7 @@ | ||
18 | 18 | $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> |
19 | 19 | ) |
20 | 20 | |
21 | -add_library(posixpp_static STATIC empty.cpp) | |
21 | +add_library(posixpp_static STATIC empty.cpp pubincludes/posixpp/simpleio.h) | |
22 | 22 | set_property(TARGET posixpp_static PROPERTY CXX_EXTENSIONS OFF) |
23 | 23 | target_compile_features(posixpp_static PUBLIC cxx_std_20) |
24 | 24 | target_include_directories(posixpp_static PUBLIC |
@@ -32,7 +32,7 @@ | ||
32 | 32 | pubincludes/pppbase/flagset.h tests/flagset.cpp |
33 | 33 | pubincludes/syscalls/linux/x86_64/fdflags.h tests/fdflags.cpp |
34 | 34 | pubincludes/syscalls/linux/x86_64/modeflags.h |
35 | - pubincludes/posixpp/modeflags.h pubincludes/syscalls/linux/basic.h pubincludes/posixpp/basic.h) | |
35 | + pubincludes/posixpp/modeflags.h pubincludes/syscalls/linux/basic.h pubincludes/posixpp/basic.h pubincludes/posixpp/simpleio.h) | |
36 | 36 | set_property(TARGET all_tests PROPERTY CXX_EXTENSIONS OFF) |
37 | 37 | target_compile_features(all_tests PUBLIC cxx_std_20) |
38 | 38 | target_link_libraries(all_tests Catch2::Catch2 fmt::fmt posixpp) |
@@ -1,12 +1,12 @@ | ||
1 | 1 | #include <posixpp/fd.h> |
2 | -#include <posixpp/expected.h> | |
3 | 2 | #include <posixpp/basic.h> |
3 | +#include <posixpp/simpleio.h> | |
4 | 4 | |
5 | 5 | int main(int argc, char const * const *argv) |
6 | 6 | { |
7 | 7 | const ::posixpp::fd fdout{1}; |
8 | 8 | static constexpr char msg[] = "Hello World!\n"; |
9 | 9 | // sizeof(msg) - 1 to skip trailing '\0' |
10 | - auto result = fdout.write(msg, sizeof(msg) - 1); | |
10 | + auto result = write(fdout, msg, sizeof(msg) - 1); | |
11 | 11 | ::posixpp::exit(result.has_error()); |
12 | 12 | } |
@@ -78,20 +78,6 @@ | ||
78 | 78 | } |
79 | 79 | } |
80 | 80 | |
81 | - expected<::std::size_t> | |
82 | - write(char const *buf, ::std::size_t size) const noexcept { | |
83 | - using posixpp::error_cascade; | |
84 | - return error_cascade(::syscalls::linux::write(fd_, buf, size), | |
85 | - [](auto r) { return static_cast<::std::size_t>(r);}); | |
86 | - } | |
87 | - | |
88 | - expected<::std::size_t> | |
89 | - read(char *buf, ::std::size_t size) const noexcept { | |
90 | - using posixpp::error_cascade; | |
91 | - return error_cascade(::syscalls::linux::read(fd_, buf, size), | |
92 | - [](auto r) { return static_cast<::std::size_t>(r);}); | |
93 | - } | |
94 | - | |
95 | 81 | //! \brief Sets fd to invalid value and also calls close regardless of |
96 | 82 | //! whether fd is currently an invalid value. |
97 | 83 | [[nodiscard]] expected<void> close() noexcept { |
@@ -101,64 +87,6 @@ | ||
101 | 87 | return close(tmpfd); |
102 | 88 | } |
103 | 89 | |
104 | - | |
105 | - ///@{ | |
106 | - [[nodiscard]] static expected<fd> | |
107 | - open(char const *pathname, openflags flags) noexcept { | |
108 | - // Hard coded the value for AT_FDCWD | |
109 | - return openat(fd(-100), pathname, flags, modeflags{}); | |
110 | - } | |
111 | - [[nodiscard]] static expected<fd> | |
112 | - open(char const *pathname, fdflags flags) noexcept { | |
113 | - return open(pathname, openflags{flags}); | |
114 | - } | |
115 | - | |
116 | - [[nodiscard]] static expected<fd> | |
117 | - open(char const *pathname, openflags flags, modeflags mode) noexcept | |
118 | - { | |
119 | - return openat(fd(-100), pathname, flags, mode); | |
120 | - } | |
121 | - [[nodiscard]] static expected<fd> | |
122 | - open(char const *pathname, fdflags flags, modeflags mode) noexcept | |
123 | - { | |
124 | - return open(pathname, openflags{flags}, mode); | |
125 | - } | |
126 | - | |
127 | - [[nodiscard]] static expected<fd> | |
128 | - openat(fd const &dirfd, char const *pathname, openflags flags) noexcept | |
129 | - { | |
130 | - using ::syscalls::linux::openat; | |
131 | - using posixpp::error_cascade; | |
132 | - auto const dfd = dirfd.as_fd(); | |
133 | - return error_cascade(openat(dfd, pathname, flags.getbits(), 0), | |
134 | - int_to_fd); | |
135 | - } | |
136 | - [[nodiscard]] static expected<fd> | |
137 | - openat(fd const &dirfd, char const *pathname, fdflags flags) noexcept | |
138 | - { | |
139 | - return openat(dirfd, pathname, openflags{flags}); | |
140 | - } | |
141 | - | |
142 | - [[nodiscard]] static expected<fd> | |
143 | - openat(fd const &dirfd, char const *pathname, | |
144 | - openflags flags, modeflags mode) noexcept | |
145 | - { | |
146 | - using ::syscalls::linux::openat; | |
147 | - using posixpp::error_cascade; | |
148 | - return error_cascade(openat(dirfd.as_fd(), | |
149 | - pathname, | |
150 | - flags.getbits(), | |
151 | - mode.getbits()), | |
152 | - int_to_fd); | |
153 | - } | |
154 | - [[nodiscard]] static expected<fd> | |
155 | - openat(fd const &dirfd, char const *pathname, | |
156 | - fdflags flags, modeflags mode) noexcept | |
157 | - { | |
158 | - return openat(dirfd, pathname, openflags{flags}, mode); | |
159 | - } | |
160 | - ///@} | |
161 | - | |
162 | 90 | protected: |
163 | 91 | static fd int_to_fd(int fdes) noexcept { |
164 | 92 | return fd{fdes}; |
@@ -0,0 +1,87 @@ | ||
1 | +// Copyright 2021 Eric Hopper | |
2 | +// Distributed under the terms of the LGPLv3. | |
3 | + | |
4 | +#pragma once | |
5 | + | |
6 | +#include <posixpp/fd.h> | |
7 | +#include <syscalls/linux/simple_io.h> | |
8 | + | |
9 | +namespace posixpp { | |
10 | + | |
11 | +//! See write(2) | |
12 | +expected<::std::size_t> | |
13 | +inline write(fd const &file, char const *buf, ::std::size_t size) noexcept { | |
14 | + using posixpp::error_cascade; | |
15 | + return error_cascade(::syscalls::linux::write(file.as_fd(), buf, size), | |
16 | + [](auto r) { return static_cast<::std::size_t>(r);}); | |
17 | +} | |
18 | + | |
19 | +//! See read(2) | |
20 | +expected<::std::size_t> | |
21 | +inline read(fd const &file, char *buf, ::std::size_t size) noexcept { | |
22 | + using posixpp::error_cascade; | |
23 | + return error_cascade(::syscalls::linux::read(file.as_fd(), buf, size), | |
24 | + [](auto r) { return static_cast<::std::size_t>(r);}); | |
25 | +} | |
26 | + | |
27 | +///@{ | |
28 | +[[nodiscard]] expected<fd> | |
29 | +inline openat(fd const &dirfd, char const *pathname, | |
30 | + openflags flags, modeflags mode) noexcept | |
31 | +{ | |
32 | + // What all the other versions turn into, so it has to be first. | |
33 | + using ::syscalls::linux::openat; | |
34 | + using posixpp::error_cascade; | |
35 | + return error_cascade(openat(dirfd.as_fd(), | |
36 | + pathname, | |
37 | + flags.getbits(), | |
38 | + mode.getbits()), | |
39 | + [](int fdint){return fd{fdint};}); | |
40 | +} | |
41 | + | |
42 | +[[nodiscard]] expected<fd> | |
43 | +inline open(char const *pathname, openflags flags) noexcept { | |
44 | + // Hard coded the value for AT_FDCWD | |
45 | + return openat(fd(-100), pathname, flags, modeflags{}); | |
46 | +} | |
47 | +[[nodiscard]] expected<fd> | |
48 | +inline open(char const *pathname, fdflags flags) noexcept { | |
49 | + return open(pathname, openflags{flags}); | |
50 | +} | |
51 | + | |
52 | +[[nodiscard]] expected<fd> | |
53 | +inline open(char const *pathname, openflags flags, modeflags mode) noexcept | |
54 | +{ | |
55 | + return openat(fd(-100), pathname, flags, mode); | |
56 | +} | |
57 | +[[nodiscard]] expected<fd> | |
58 | +inline open(char const *pathname, fdflags flags, modeflags mode) noexcept | |
59 | +{ | |
60 | + return open(pathname, openflags{flags}, mode); | |
61 | +} | |
62 | + | |
63 | +[[nodiscard]] expected<fd> | |
64 | +inline openat(fd const &dirfd, char const *pathname, openflags flags) noexcept | |
65 | +{ | |
66 | + using ::syscalls::linux::openat; | |
67 | + using posixpp::error_cascade; | |
68 | + auto const dfd = dirfd.as_fd(); | |
69 | + return error_cascade(openat(dfd, pathname, flags.getbits(), 0), | |
70 | + [](int fdint){return fd{fdint};}); | |
71 | +} | |
72 | + | |
73 | +[[nodiscard]] expected<fd> | |
74 | +inline openat(fd const &dirfd, char const *pathname, fdflags flags) noexcept | |
75 | +{ | |
76 | + return openat(dirfd, pathname, openflags{flags}); | |
77 | +} | |
78 | + | |
79 | +[[nodiscard]] expected<fd> | |
80 | +inline openat(fd const &dirfd, char const *pathname, | |
81 | + fdflags flags, modeflags mode) noexcept | |
82 | +{ | |
83 | + return openat(dirfd, pathname, openflags{flags}, mode); | |
84 | +} | |
85 | +///@} | |
86 | + | |
87 | +} |
@@ -1,6 +1,7 @@ | ||
1 | 1 | #include <algorithm> |
2 | 2 | #include <posixpp/fd.h> |
3 | 3 | #include <posixpp/expected.h> |
4 | +#include <posixpp/simpleio.h> | |
4 | 5 | #include <fcntl.h> |
5 | 6 | #include <unistd.h> |
6 | 7 | #include "tempdir.h" |
@@ -19,8 +20,9 @@ | ||
19 | 20 | using of = ::posixpp::openflags; |
20 | 21 | using fdf = ::posixpp::fdflags; |
21 | 22 | using ::posixpp::modeflags; |
23 | + using ::posixpp::open; | |
22 | 24 | auto foo{ |
23 | - fd::open( | |
25 | + open( | |
24 | 26 | fooname.native().c_str(), |
25 | 27 | of::creat | fdf::wronly, |
26 | 28 | modeflags::irwall |
@@ -49,18 +51,18 @@ | ||
49 | 51 | "the same as the data read, and no more data can be read" |
50 | 52 | ) { |
51 | 53 | // sizeof(msg) - 1 to skip the trailing '\8' |
52 | - auto const write_result = foo.write(msg, sizeof(msg) - 1); | |
54 | + auto const write_result = write(foo, msg, sizeof(msg) - 1); | |
53 | 55 | REQUIRE(write_result.result() == sizeof(msg) - 1); |
54 | 56 | auto const close_result = foo.close(); |
55 | 57 | REQUIRE_FALSE(close_result.has_error()); |
56 | 58 | REQUIRE_FALSE(foo.is_valid()); |
57 | 59 | |
58 | - foo = fd::open(fooname.native().c_str(), fdf::rdonly).result(); | |
60 | + foo = open(fooname.native().c_str(), fdf::rdonly).result(); | |
59 | 61 | REQUIRE(foo.is_valid()); |
60 | 62 | |
61 | 63 | // Try to read more than was written to make sure we can't read |
62 | 64 | // extra (as per the description in the WHEN clause above). |
63 | - auto read_result = foo.read(readmsg, sizeof(readmsg)); | |
65 | + auto read_result = read(foo, readmsg, sizeof(readmsg)); | |
64 | 66 | REQUIRE(read_result.result() == sizeof(readmsg) - 1); |
65 | 67 | REQUIRE(::std::equal(msg, msg + (sizeof(msg) - 1), readmsg)); |
66 | 68 | } |
@@ -82,17 +84,18 @@ | ||
82 | 84 | using of = ::posixpp::openflags; |
83 | 85 | using fdf = ::posixpp::fdflags; |
84 | 86 | using ::posixpp::modeflags; |
87 | + using ::posixpp::open; | |
85 | 88 | auto foo{ |
86 | - fd::open( | |
89 | + open( | |
87 | 90 | fooname.native().c_str(), |
88 | 91 | of::creat | fdf::wronly, |
89 | 92 | modeflags::irwall |
90 | 93 | ).result() |
91 | 94 | }; |
92 | 95 | // Don't write out the trailing '\0'; |
93 | - REQUIRE(foo.write(known_text, sizeof(known_text) - 1).result() == sizeof(known_text) - 1); | |
96 | + REQUIRE(write(foo, known_text, sizeof(known_text) - 1).result() == sizeof(known_text) - 1); | |
94 | 97 | foo.close().throw_if_error(); |
95 | - foo = fd::open(fooname.native().c_str(), fdf::rdonly).result(); | |
98 | + foo = open(fooname.native().c_str(), fdf::rdonly).result(); | |
96 | 99 | WHEN("The foo.dup() is called.") { |
97 | 100 | fd bar{ foo.dup().result() }; |
98 | 101 |
@@ -102,13 +105,13 @@ | ||
102 | 105 | } |
103 | 106 | AND_WHEN("You read one character from it.") { |
104 | 107 | char buf[1]; |
105 | - REQUIRE(bar.read(buf, 1).result() == 1); | |
108 | + REQUIRE(read(bar, buf, 1).result() == 1); | |
106 | 109 | THEN("you read the first character of the known text.") { |
107 | 110 | REQUIRE(buf[0] == known_text[0]); |
108 | 111 | } |
109 | 112 | AND_WHEN("you then read one character from the original fd.") { |
110 | 113 | char buf2[1]; |
111 | - REQUIRE(foo.read(buf2, 1).result() == 1); | |
114 | + REQUIRE(read(foo, buf2, 1).result() == 1); | |
112 | 115 | THEN("that character is the second character of the known text.") { |
113 | 116 | REQUIRE(buf2[0] == known_text[1]); |
114 | 117 | } |
@@ -122,12 +125,12 @@ | ||
122 | 125 | } |
123 | 126 | THEN( "a byte read from foo is the first byte of known_text") { |
124 | 127 | char buf_foo[1]; |
125 | - REQUIRE(foo.read(buf_foo, 1).result() == 1); | |
128 | + REQUIRE(read(foo, buf_foo, 1).result() == 1); | |
126 | 129 | REQUIRE(buf_foo[0] == known_text[0]); |
127 | 130 | } |
128 | 131 | } |
129 | 132 | AND_GIVEN("Another file descriptor opened on the same file.") { |
130 | - auto foo2{fd::open(fooname.native().c_str(), fdf::rdonly).result()}; | |
133 | + auto foo2{open(fooname.native().c_str(), fdf::rdonly).result()}; | |
131 | 134 | THEN("It is valid.") { |
132 | 135 | REQUIRE(foo2.is_valid()); |
133 | 136 | } |
@@ -137,8 +140,8 @@ | ||
137 | 140 | // Initialize to 0 to make sure they change when read into. |
138 | 141 | char buf_foo[1] = {}; |
139 | 142 | char buf_foo2[1] = {}; |
140 | - REQUIRE(foo.read(buf_foo, 1).result() == 1); | |
141 | - REQUIRE(foo2.read(buf_foo2, 1).result() == 1); | |
143 | + REQUIRE(read(foo, buf_foo, 1).result() == 1); | |
144 | + REQUIRE(read(foo2, buf_foo2, 1).result() == 1); | |
142 | 145 | THEN( |
143 | 146 | "it's the same byte, and the first byte of the known " |
144 | 147 | "text, demonstrating that they each refer to a different " |
@@ -154,8 +157,8 @@ | ||
154 | 157 | } AND_WHEN ("we then read read a byte from each file.") { |
155 | 158 | char buff2_foo[1]; |
156 | 159 | char buf2_foo2[1]; |
157 | - REQUIRE(foo.read(buff2_foo, 1).result() == 1); | |
158 | - REQUIRE(foo2.read(buf2_foo2, 1).result() == 1); | |
160 | + REQUIRE(read(foo, buff2_foo, 1).result() == 1); | |
161 | + REQUIRE(read(foo2, buf2_foo2, 1).result() == 1); | |
159 | 162 | THEN( |
160 | 163 | "then we get the 2nd and 3rd bytes of known " |
161 | 164 | "text, demonstrating they now refer to the same " |