1 /* Example of Outcome used with constructors
2 (C) 2017-2019 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3
4
5 Boost Software License - Version 1.0 - August 17th, 2003
6
7 Permission is hereby granted, free of charge, to any person or organization
8 obtaining a copy of the software and accompanying documentation covered by
9 this license (the "Software") to use, reproduce, display, distribute,
10 execute, and transmit the Software, and to prepare derivative works of the
11 Software, and to permit third-parties to whom the Software is furnished to
12 do so, all subject to the following:
13
14 The copyright notices in the Software and this entire statement, including
15 the above license grant, this restriction and the following disclaimer,
16 must be included in all copies of the Software, in whole or in part, and
17 all derivative works of the Software, unless such copies or derivative
18 works are solely in the form of machine-executable object code generated by
19 a source language processor.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
24 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
25 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
26 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 DEALINGS IN THE SOFTWARE.
28 */
29
30 #ifdef _WIN32
31 #define _CRT_SECURE_NO_WARNINGS
32 #define _CRT_NONSTDC_NO_WARNINGS
33 #include <fcntl.h>
34 #include <io.h>
35 #else
36 #define file_handle linux_file_handle
37 #include <fcntl.h>
38 #include <unistd.h>
39 #undef file_handle
40 #endif
41 #include <cstring> // for strerror
42 #include <sys/stat.h>
43 #include <sys/types.h>
44
45 #include "../../../include/boost/outcome.hpp"
46 #include <experimental/filesystem>
47
48 namespace outcome = BOOST_OUTCOME_V2_NAMESPACE;
49 namespace filesystem = std::experimental::filesystem;
50
51 //! [file_handle]
52 class file_handle
53 {
54 int _fd{-1}; // file descriptor
55 struct stat _stat
56 {
57 0
58 }; // stat of the fd at open
59
60 // Phase 1 private constexpr constructor
file_handle()61 constexpr file_handle() {}
62
63 public:
64 using path_type = filesystem::path;
65
66 //! The behaviour of the handle: does it read, read and write, or atomic append?
67 enum class mode : unsigned char // bit 0 set means writable
68 {
69 unchanged = 0,
70 none = 2, //!< No ability to read or write anything, but can synchronise (SYNCHRONIZE or 0)
71 attr_read = 4, //!< Ability to read attributes (FILE_READ_ATTRIBUTES|SYNCHRONIZE or O_RDONLY)
72 attr_write = 5, //!< Ability to read and write attributes (FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|SYNCHRONIZE or O_RDONLY)
73 read = 6, //!< Ability to read (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|SYNCHRONISE or O_RDONLY)
74 write = 7, //!< Ability to read and write (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONISE or O_RDWR)
75 append = 9 //!< All mainstream OSs and CIFS guarantee this is atomic with respect to all other appenders (FILE_APPEND_DATA|SYNCHRONISE or O_APPEND)
76 };
77
78 // Moves but not copies permitted
79 file_handle(const file_handle &) = delete;
file_handle(file_handle && o)80 file_handle(file_handle &&o) noexcept : _fd(o._fd) { o._fd = -1; }
81 file_handle &operator=(const file_handle &) = delete;
operator =(file_handle && o)82 file_handle &operator=(file_handle &&o) noexcept
83 {
84 this->~file_handle();
85 new(this) file_handle(std::move(o));
86 return *this;
87 }
88 // Destruction closes the handle
~file_handle()89 ~file_handle()
90 {
91 if(_fd != -1)
92 {
93 if(-1 == ::close(_fd))
94 {
95 int e = errno;
96 std::cerr << "FATAL: Closing the fd during destruction failed due to " << strerror(e) << std::endl;
97 std::terminate();
98 }
99 _fd = -1;
100 }
101 }
102
103 // Phase 2 static member constructor function, which cannot throw
104 static inline outcome::result<file_handle> file(path_type path, mode mode = mode::read) noexcept;
105 };
106 //! [file_handle]
107
108 //! [file]
109 // Phase 2 static member constructor function, which cannot throw
file(file_handle::path_type path,file_handle::mode mode)110 inline outcome::result<file_handle> file_handle::file(file_handle::path_type path, file_handle::mode mode) noexcept
111 {
112 // Perform phase 1 of object construction
113 file_handle ret;
114
115 // Perform phase 2 of object construction
116 int flags = 0;
117 switch(mode)
118 {
119 case mode::attr_read:
120 case mode::read:
121 flags = O_RDONLY;
122 break;
123 case mode::attr_write:
124 case mode::write:
125 flags = O_RDWR;
126 break;
127 case mode::append:
128 flags = O_APPEND;
129 break;
130 default:
131 return std::errc::invalid_argument;
132 }
133 ret._fd = ::open(path.u8string().c_str(), flags);
134 if(-1 == ret._fd)
135 {
136 // Note that if we bail out here, ~file_handle() will correctly not call ::close()
137 return {errno, std::system_category()};
138 }
139 if(-1 == ::fstat(ret._fd, &ret._stat))
140 {
141 // Note that if we bail out here, ~file_handle() will correctly call ::close()
142 return {errno, std::system_category()};
143 }
144
145 // Returning ret directly is an area full of compiler specific behaviour quirks,
146 // so be explicit by wrapping into an initialiser list with embedded move.
147 return {std::move(ret)};
148 }
149 //! [file]
150
151 //! [construct-declaration]
152 template <class T> struct make
153 {
operator ()make154 outcome::result<T> operator()() const noexcept
155 { //
156 static_assert(!std::is_same<T, T>::value, //
157 "make<T>() was not specialised for the type T supplied");
158 }
159 };
160 //! [construct-declaration]
161
162 //! [construct-specialisation]
163 template <> struct make<file_handle>
164 {
165 file_handle::path_type _path;
166 file_handle::mode _mode{file_handle::mode::read};
167 // Any other args, default initialised if necessary, follow here ...
168
operator ()make169 outcome::result<file_handle> operator()() const noexcept //
170 {
171 return file_handle::file(std::move(_path));
172 }
173 };
174 //! [construct-specialisation]
175
main()176 int main()
177 {
178 //! [static-use]
179 outcome::result<file_handle> fh1 = file_handle::file("hello" /*, file_handle::mode::read */);
180 if(!fh1)
181 {
182 std::cerr << "Opening file 'hello' failed with " << fh1.error().message() << std::endl;
183 }
184 //! [static-use]
185 //! [construct-use]
186 outcome::result<file_handle> fh2 = make<file_handle>{"hello" /*, file_handle::mode::read */}();
187 if(!fh2)
188 {
189 std::cerr << "Opening file 'hello' failed with " << fh2.error().message() << std::endl;
190 }
191 //! [construct-use]
192 return 0;
193 }