]>
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 uses exception handling. The version that does not use exception | |
10 | // handling is in print_file_result.cpp. | |
11 | ||
12 | #include <boost/leaf/exception.hpp> | |
13 | #include <boost/leaf/handle_errors.hpp> | |
14 | #include <boost/leaf/pred.hpp> | |
15 | #include <boost/leaf/on_error.hpp> | |
16 | #include <boost/leaf/common.hpp> | |
17 | #include <iostream> | |
18 | #include <memory> | |
19 | #include <stdio.h> | |
20 | ||
21 | namespace leaf = boost::leaf; | |
22 | ||
23 | ||
24 | // Error types | |
25 | struct bad_command_line { }; | |
26 | struct input_error { }; | |
27 | struct open_error { }; | |
28 | struct read_error { }; | |
29 | struct size_error { }; | |
30 | struct eof_error { }; | |
31 | struct output_error { }; | |
32 | ||
33 | ||
34 | // We will handle all failures in our main function, but first, here are the declarations of the functions it calls, each | |
35 | // communicating failures by throwing exceptions | |
36 | ||
37 | // Parse the command line, return the file name. | |
38 | char const * parse_command_line( int argc, char const * argv[] ); | |
39 | ||
40 | // Open a file for reading. | |
41 | std::shared_ptr<FILE> file_open( char const * file_name ); | |
42 | ||
43 | // Return the size of the file. | |
44 | int file_size( FILE & f ); | |
45 | ||
46 | // Read size bytes from f into buf. | |
47 | void file_read( FILE & f, void * buf, int size ); | |
48 | ||
49 | ||
50 | // The main function, which handles all errors. | |
51 | int main( int argc, char const * argv[] ) | |
52 | { | |
53 | return leaf::try_catch( | |
54 | ||
55 | [&] | |
56 | { | |
57 | char const * file_name = parse_command_line(argc,argv); | |
58 | ||
59 | auto load = leaf::on_error( leaf::e_file_name{file_name} ); | |
60 | ||
61 | std::shared_ptr<FILE> f = file_open(file_name); | |
62 | ||
63 | int s = file_size(*f); | |
64 | ||
65 | std::string buffer(1 + s, '\0'); | |
66 | file_read(*f, &buffer[0], buffer.size()-1); | |
67 | ||
68 | std::cout << buffer; | |
69 | std::cout.flush(); | |
70 | if( std::cout.fail() ) | |
71 | throw leaf::exception(output_error{}, leaf::e_errno{errno}); | |
72 | ||
73 | return 0; | |
74 | }, | |
75 | ||
76 | // Each of the lambdas below is an error handler. LEAF will consider them, in order, and call the first one that matches | |
77 | // the available error objects. | |
78 | ||
79 | // This handler will be called if the error includes: | |
80 | // - an object of type open_error, and | |
81 | // - an object of type leaf::e_errno that has .value equal to ENOENT, and | |
82 | // - an object of type leaf::e_file_name. | |
83 | []( open_error &, leaf::match_value<leaf::e_errno, ENOENT>, leaf::e_file_name const & fn ) | |
84 | { | |
85 | std::cerr << "File not found: " << fn.value << std::endl; | |
86 | return 1; | |
87 | }, | |
88 | ||
89 | // This handler will be called if the error includes: | |
90 | // - an object of type open_error, and | |
91 | // - an object of type leaf::e_errno (regardless of its .value), and | |
92 | // - an object of type leaf::e_file_name. | |
93 | []( open_error &, leaf::e_errno const & errn, leaf::e_file_name const & fn ) | |
94 | { | |
95 | std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; | |
96 | return 2; | |
97 | }, | |
98 | ||
99 | // This handler will be called if the error includes: | |
100 | // - an object of type input_error, and | |
101 | // - an optional object of type leaf::e_errno (regardless of its .value), and | |
102 | // - an object of type leaf::e_file_name. | |
103 | []( input_error &, leaf::e_errno const * errn, leaf::e_file_name const & fn ) | |
104 | { | |
105 | std::cerr << "Failed to access " << fn.value; | |
106 | if( errn ) | |
107 | std::cerr << ", errno=" << *errn; | |
108 | std::cerr << std::endl; | |
109 | return 3; | |
110 | }, | |
111 | ||
112 | // This handler will be called if the error includes: | |
113 | // - an object of type output_error, and | |
114 | // - an object of type leaf::e_errno (regardless of its .value), | |
115 | []( output_error &, leaf::e_errno const & errn ) | |
116 | { | |
117 | std::cerr << "Output error, errno=" << errn << std::endl; | |
118 | return 4; | |
119 | }, | |
120 | ||
121 | // This handler will be called if we've got a bad_command_line | |
122 | []( bad_command_line & ) | |
123 | { | |
124 | std::cout << "Bad command line argument" << std::endl; | |
125 | return 5; | |
126 | }, | |
127 | ||
128 | // This last handler matches any error: it prints diagnostic information to help debug logic errors in the program, since it | |
129 | // failed to match an appropriate error handler to the error condition it encountered. In this program this handler will | |
130 | // never be called. | |
131 | []( leaf::error_info const & unmatched ) | |
132 | { | |
133 | std::cerr << | |
134 | "Unknown failure detected" << std::endl << | |
135 | "Cryptic diagnostic information follows" << std::endl << | |
136 | unmatched; | |
137 | return 6; | |
138 | } ); | |
139 | } | |
140 | ||
141 | ||
142 | // Implementations of the functions called by main: | |
143 | ||
144 | ||
145 | // Parse the command line, return the file name. | |
146 | char const * parse_command_line( int argc, char const * argv[] ) | |
147 | { | |
148 | if( argc==2 ) | |
149 | return argv[1]; | |
150 | else | |
151 | throw leaf::exception(bad_command_line{}); | |
152 | } | |
153 | ||
154 | ||
155 | // Open a file for reading. | |
156 | std::shared_ptr<FILE> file_open( char const * file_name ) | |
157 | { | |
158 | auto load = leaf::on_error(input_error{}); | |
159 | ||
160 | if( FILE * f = fopen(file_name, "rb") ) | |
161 | return std::shared_ptr<FILE>(f, &fclose); | |
162 | else | |
163 | throw leaf::exception(open_error{}, leaf::e_errno{errno}); | |
164 | } | |
165 | ||
166 | ||
167 | // Return the size of the file. | |
168 | int file_size( FILE & f ) | |
169 | { | |
170 | auto load = leaf::on_error(input_error{}, size_error{}, []{ return leaf::e_errno{errno}; }); | |
171 | ||
172 | if( fseek(&f, 0, SEEK_END) ) | |
173 | throw leaf::exception(); | |
174 | ||
175 | int s = ftell(&f); | |
176 | if( s==-1L ) | |
177 | throw leaf::exception(); | |
178 | ||
179 | if( fseek(&f,0,SEEK_SET) ) | |
180 | throw leaf::exception(); | |
181 | ||
182 | return s; | |
183 | } | |
184 | ||
185 | ||
186 | // Read size bytes from f into buf. | |
187 | void file_read( FILE & f, void * buf, int size ) | |
188 | { | |
189 | auto load = leaf::on_error(input_error{}); | |
190 | ||
191 | int n = fread(buf, 1, size, &f); | |
192 | ||
193 | if( ferror(&f) ) | |
194 | throw leaf::exception(read_error{}, leaf::e_errno{errno}); | |
195 | ||
196 | if( n!=size ) | |
197 | throw leaf::exception(eof_error{}); | |
198 | } |