]>
Commit | Line | Data |
---|---|---|
1e59de90 | 1 | // Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. |
20effc67 TL |
2 | |
3 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
4 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
5 | ||
1e59de90 TL |
6 | // This is the program presented in |
7 | // https://boostorg.github.io/leaf/#introduction-result. | |
20effc67 | 8 | |
1e59de90 TL |
9 | // It reads a text file in a buffer and prints it to std::cout, using LEAF to |
10 | // handle errors. This version does not use exception handling. The version that | |
11 | // does use exception handling is in print_file_eh.cpp. | |
20effc67 | 12 | |
1e59de90 | 13 | #include <boost/leaf.hpp> |
20effc67 | 14 | #include <iostream> |
20effc67 TL |
15 | #include <stdio.h> |
16 | ||
17 | namespace leaf = boost::leaf; | |
18 | ||
19 | ||
20 | // First, we need an enum to define our error codes: | |
21 | enum error_code | |
22 | { | |
23 | bad_command_line = 1, | |
24 | open_error, | |
25 | read_error, | |
26 | size_error, | |
27 | eof_error, | |
28 | output_error | |
29 | }; | |
30 | ||
31 | ||
32 | template <class T> | |
33 | using result = leaf::result<T>; | |
34 | ||
35 | ||
1e59de90 TL |
36 | // We will handle all failures in our main function, but first, here are the |
37 | // declarations of the functions it calls, each communicating failures using | |
38 | // result<T>: | |
20effc67 TL |
39 | |
40 | // Parse the command line, return the file name. | |
41 | result<char const *> parse_command_line( int argc, char const * argv[] ); | |
42 | ||
43 | // Open a file for reading. | |
44 | result<std::shared_ptr<FILE>> file_open( char const * file_name ); | |
45 | ||
46 | // Return the size of the file. | |
47 | result<int> file_size( FILE & f ); | |
48 | ||
49 | // Read size bytes from f into buf. | |
50 | result<void> file_read( FILE & f, void * buf, int size ); | |
51 | ||
52 | ||
53 | // The main function, which handles all errors. | |
54 | int main( int argc, char const * argv[] ) | |
55 | { | |
56 | return leaf::try_handle_all( | |
57 | ||
58 | [&]() -> result<int> | |
59 | { | |
60 | BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); | |
61 | ||
62 | auto load = leaf::on_error( leaf::e_file_name{file_name} ); | |
63 | ||
64 | BOOST_LEAF_AUTO(f, file_open(file_name)); | |
65 | ||
66 | BOOST_LEAF_AUTO(s, file_size(*f)); | |
67 | ||
68 | std::string buffer(1 + s, '\0'); | |
69 | BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); | |
70 | ||
71 | std::cout << buffer; | |
72 | std::cout.flush(); | |
73 | if( std::cout.fail() ) | |
74 | return leaf::new_error(output_error, leaf::e_errno{errno}); | |
75 | ||
76 | return 0; | |
77 | }, | |
78 | ||
1e59de90 TL |
79 | // Each of the lambdas below is an error handler. LEAF will consider |
80 | // them, in order, and call the first one that matches the available | |
81 | // error objects. | |
20effc67 TL |
82 | |
83 | // This handler will be called if the error includes: | |
84 | // - an object of type error_code equal to open_error, and | |
1e59de90 TL |
85 | // - an object of type leaf::e_errno that has .value equal to ENOENT, |
86 | // and | |
20effc67 TL |
87 | // - an object of type leaf::e_file_name. |
88 | []( leaf::match<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn ) | |
89 | { | |
90 | std::cerr << "File not found: " << fn.value << std::endl; | |
91 | return 1; | |
92 | }, | |
93 | ||
94 | // This handler will be called if the error includes: | |
95 | // - an object of type error_code equal to open_error, and | |
96 | // - an object of type leaf::e_errno (regardless of its .value), and | |
97 | // - an object of type leaf::e_file_name. | |
98 | []( leaf::match<error_code, open_error>, leaf::e_errno const & errn, leaf::e_file_name const & fn ) | |
99 | { | |
100 | std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; | |
101 | return 2; | |
102 | }, | |
103 | ||
104 | // This handler will be called if the error includes: | |
1e59de90 TL |
105 | // - an object of type error_code equal to any of size_error, |
106 | // read_error, eof_error, and | |
107 | // - an optional object of type leaf::e_errno (regardless of its | |
108 | // .value), and | |
20effc67 TL |
109 | // - an object of type leaf::e_file_name. |
110 | []( leaf::match<error_code, size_error, read_error, eof_error>, leaf::e_errno const * errn, leaf::e_file_name const & fn ) | |
111 | { | |
112 | std::cerr << "Failed to access " << fn.value; | |
113 | if( errn ) | |
114 | std::cerr << ", errno=" << *errn; | |
115 | std::cerr << std::endl; | |
116 | return 3; | |
117 | }, | |
118 | ||
119 | // This handler will be called if the error includes: | |
120 | // - an object of type error_code equal to output_error, and | |
121 | // - an object of type leaf::e_errno (regardless of its .value), | |
122 | []( leaf::match<error_code, output_error>, leaf::e_errno const & errn ) | |
123 | { | |
124 | std::cerr << "Output error, errno=" << errn << std::endl; | |
125 | return 4; | |
126 | }, | |
127 | ||
128 | // This handler will be called if we've got a bad_command_line | |
129 | []( leaf::match<error_code, bad_command_line> ) | |
130 | { | |
131 | std::cout << "Bad command line argument" << std::endl; | |
132 | return 5; | |
133 | }, | |
134 | ||
1e59de90 TL |
135 | // This last handler matches any error: it prints diagnostic information |
136 | // to help debug logic errors in the program, since it failed to match | |
137 | // an appropriate error handler to the error condition it encountered. | |
138 | // In this program this handler will never be called. | |
20effc67 TL |
139 | []( leaf::error_info const & unmatched ) |
140 | { | |
141 | std::cerr << | |
142 | "Unknown failure detected" << std::endl << | |
143 | "Cryptic diagnostic information follows" << std::endl << | |
144 | unmatched; | |
145 | return 6; | |
146 | } ); | |
147 | } | |
148 | ||
149 | ||
150 | // Implementations of the functions called by main: | |
151 | ||
152 | ||
153 | // Parse the command line, return the file name. | |
154 | result<char const *> parse_command_line( int argc, char const * argv[] ) | |
155 | { | |
156 | if( argc==2 ) | |
157 | return argv[1]; | |
158 | else | |
159 | return leaf::new_error(bad_command_line); | |
160 | } | |
161 | ||
162 | ||
163 | // Open a file for reading. | |
164 | result<std::shared_ptr<FILE>> file_open( char const * file_name ) | |
165 | { | |
166 | if( FILE * f = fopen(file_name, "rb") ) | |
167 | return std::shared_ptr<FILE>(f, &fclose); | |
168 | else | |
169 | return leaf::new_error(open_error, leaf::e_errno{errno}); | |
170 | } | |
171 | ||
172 | ||
173 | // Return the size of the file. | |
174 | result<int> file_size( FILE & f ) | |
175 | { | |
176 | auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); | |
177 | ||
178 | if( fseek(&f, 0, SEEK_END) ) | |
179 | return leaf::new_error(size_error); | |
180 | ||
181 | int s = ftell(&f); | |
182 | if( s==-1L ) | |
183 | return leaf::new_error(size_error); | |
184 | ||
185 | if( fseek(&f,0,SEEK_SET) ) | |
186 | return leaf::new_error(size_error); | |
187 | ||
188 | return s; | |
189 | } | |
190 | ||
191 | ||
192 | // Read size bytes from f into buf. | |
193 | result<void> file_read( FILE & f, void * buf, int size ) | |
194 | { | |
195 | int n = fread(buf, 1, size, &f); | |
196 | ||
197 | if( ferror(&f) ) | |
198 | return leaf::new_error(read_error, leaf::e_errno{errno}); | |
199 | ||
200 | if( n!=size ) | |
201 | return leaf::new_error(eof_error); | |
202 | ||
203 | return { }; | |
204 | } | |
205 | ||
206 | //////////////////////////////////////// | |
207 | ||
208 | #ifdef BOOST_LEAF_NO_EXCEPTIONS | |
209 | ||
210 | namespace boost | |
211 | { | |
212 | BOOST_LEAF_NORETURN void throw_exception( std::exception const & e ) | |
213 | { | |
214 | std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); | |
215 | std::terminate(); | |
216 | } | |
217 | ||
218 | struct source_location; | |
219 | BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) | |
220 | { | |
221 | throw_exception(e); | |
222 | } | |
223 | } | |
224 | ||
225 | #endif |