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