From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- .../libs/leaf/example/print_file/print_file_eh.cpp | 198 +++++++++++++++++ .../print_file/print_file_outcome_result.cpp | 233 +++++++++++++++++++++ .../leaf/example/print_file/print_file_result.cpp | 225 ++++++++++++++++++++ src/boost/libs/leaf/example/print_file/readme.md | 16 ++ 4 files changed, 672 insertions(+) create mode 100644 src/boost/libs/leaf/example/print_file/print_file_eh.cpp create mode 100644 src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp create mode 100644 src/boost/libs/leaf/example/print_file/print_file_result.cpp create mode 100644 src/boost/libs/leaf/example/print_file/readme.md (limited to 'src/boost/libs/leaf/example/print_file') diff --git a/src/boost/libs/leaf/example/print_file/print_file_eh.cpp b/src/boost/libs/leaf/example/print_file/print_file_eh.cpp new file mode 100644 index 000000000..69361e40e --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_eh.cpp @@ -0,0 +1,198 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This is the program presented in +// https://boostorg.github.io/leaf/#introduction-eh. + +// It reads a text file in a buffer and prints it to std::cout, using LEAF to +// handle errors. This version uses exception handling. The version that does +// not use exception handling is in print_file_result.cpp. + +#include +#include +#include + +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures by +// throwing exceptions + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +std::shared_ptr file_open( char const * file_name ); + +// Return the size of the file. +int file_size( FILE & f ); + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_catch( + + [&] + { + char const * file_name = parse_command_line(argc,argv); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + std::shared_ptr f = file_open(file_name); + + int s = file_size(*f); + + std::string buffer(1 + s, '\0'); + file_read(*f, &buffer[0], buffer.size()-1); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + throw leaf::exception(output_error, leaf::e_errno{errno}); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::match_value, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +char const * parse_command_line( int argc, char const * argv[] ) +{ + if( argc==2 ) + return argv[1]; + else + throw leaf::exception(bad_command_line); +} + + +// Open a file for reading. +std::shared_ptr file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name, "rb") ) + return std::shared_ptr(f, &fclose); + else + throw leaf::exception(open_error, leaf::e_errno{errno}); +} + + +// Return the size of the file. +int file_size( FILE & f ) +{ + auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); + + if( fseek(&f, 0, SEEK_END) ) + throw leaf::exception(size_error); + + int s = ftell(&f); + if( s==-1L ) + throw leaf::exception(size_error); + + if( fseek(&f,0,SEEK_SET) ) + throw leaf::exception(size_error); + + return s; +} + + +// Read size bytes from f into buf. +void file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + throw leaf::exception(read_error, leaf::e_errno{errno}); + + if( n!=size ) + throw leaf::exception(eof_error); +} diff --git a/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp b/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp new file mode 100644 index 000000000..6710829df --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp @@ -0,0 +1,233 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This is the program presented in +// https://boostorg.github.io/leaf/#introduction-result, converted to use +// outcome::result instead of leaf::result. + +// It reads a text file in a buffer and prints it to std::cout, using LEAF to +// handle errors. This version does not use exception handling. + +#include +#include +#include +#include + +namespace outcome = boost::outcome_v2; +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +template +using result = outcome::std_result; + +// To enable LEAF to work with outcome::result, we need to specialize the +// is_result_type template: +namespace boost { namespace leaf { + template struct is_result_type>: std::true_type { }; +} } + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures using +// result: + +// Parse the command line, return the file name. +result parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +result> file_open( char const * file_name ); + +// Return the size of the file. +result file_size( FILE & f ); + +// Read size bytes from f into buf. +result file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_handle_all( + + [&]() -> result + { + BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + BOOST_LEAF_AUTO(f, file_open(file_name)); + + BOOST_LEAF_AUTO(s, file_size(*f)); + + std::string buffer(1 + s, '\0'); + BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error(output_error, leaf::e_errno{errno}).to_error_code(); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::match_value, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +result parse_command_line( int argc, char const * argv[] ) +{ + if( argc==2 ) + return argv[1]; + else + return leaf::new_error(bad_command_line).to_error_code(); +} + + +// Open a file for reading. +result> file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name, "rb") ) + return std::shared_ptr(f, &fclose); + else + return leaf::new_error(open_error, leaf::e_errno{errno}).to_error_code(); +} + + +// Return the size of the file. +result file_size( FILE & f ) +{ + auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); + + if( fseek(&f, 0, SEEK_END) ) + return leaf::new_error(size_error).to_error_code(); + + int s = ftell(&f); + if( s==-1L ) + return leaf::new_error(size_error).to_error_code(); + + if( fseek(&f,0,SEEK_SET) ) + return leaf::new_error(size_error).to_error_code(); + + return s; +} + + +// Read size bytes from f into buf. +result file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + return leaf::new_error(read_error, leaf::e_errno{errno}).to_error_code(); + + if( n!=size ) + return leaf::new_error(eof_error).to_error_code(); + + return outcome::success(); +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e ) + { + std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); + std::terminate(); + } + + struct source_location; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/print_file/print_file_result.cpp b/src/boost/libs/leaf/example/print_file/print_file_result.cpp new file mode 100644 index 000000000..46f5af25a --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/print_file_result.cpp @@ -0,0 +1,225 @@ +// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc. + +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// This is the program presented in +// https://boostorg.github.io/leaf/#introduction-result. + +// It reads a text file in a buffer and prints it to std::cout, using LEAF to +// handle errors. This version does not use exception handling. The version that +// does use exception handling is in print_file_eh.cpp. + +#include +#include +#include + +namespace leaf = boost::leaf; + + +// First, we need an enum to define our error codes: +enum error_code +{ + bad_command_line = 1, + open_error, + read_error, + size_error, + eof_error, + output_error +}; + + +template +using result = leaf::result; + + +// We will handle all failures in our main function, but first, here are the +// declarations of the functions it calls, each communicating failures using +// result: + +// Parse the command line, return the file name. +result parse_command_line( int argc, char const * argv[] ); + +// Open a file for reading. +result> file_open( char const * file_name ); + +// Return the size of the file. +result file_size( FILE & f ); + +// Read size bytes from f into buf. +result file_read( FILE & f, void * buf, int size ); + + +// The main function, which handles all errors. +int main( int argc, char const * argv[] ) +{ + return leaf::try_handle_all( + + [&]() -> result + { + BOOST_LEAF_AUTO(file_name, parse_command_line(argc,argv)); + + auto load = leaf::on_error( leaf::e_file_name{file_name} ); + + BOOST_LEAF_AUTO(f, file_open(file_name)); + + BOOST_LEAF_AUTO(s, file_size(*f)); + + std::string buffer(1 + s, '\0'); + BOOST_LEAF_CHECK(file_read(*f, &buffer[0], buffer.size()-1)); + + std::cout << buffer; + std::cout.flush(); + if( std::cout.fail() ) + return leaf::new_error(output_error, leaf::e_errno{errno}); + + return 0; + }, + + // Each of the lambdas below is an error handler. LEAF will consider + // them, in order, and call the first one that matches the available + // error objects. + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno that has .value equal to ENOENT, + // and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::match_value, leaf::e_file_name const & fn ) + { + std::cerr << "File not found: " << fn.value << std::endl; + return 1; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to open_error, and + // - an object of type leaf::e_errno (regardless of its .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const & errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to open " << fn.value << ", errno=" << errn << std::endl; + return 2; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to any of size_error, + // read_error, eof_error, and + // - an optional object of type leaf::e_errno (regardless of its + // .value), and + // - an object of type leaf::e_file_name. + []( leaf::match, leaf::e_errno const * errn, leaf::e_file_name const & fn ) + { + std::cerr << "Failed to access " << fn.value; + if( errn ) + std::cerr << ", errno=" << *errn; + std::cerr << std::endl; + return 3; + }, + + // This handler will be called if the error includes: + // - an object of type error_code equal to output_error, and + // - an object of type leaf::e_errno (regardless of its .value), + []( leaf::match, leaf::e_errno const & errn ) + { + std::cerr << "Output error, errno=" << errn << std::endl; + return 4; + }, + + // This handler will be called if we've got a bad_command_line + []( leaf::match ) + { + std::cout << "Bad command line argument" << std::endl; + return 5; + }, + + // This last handler matches any error: it prints diagnostic information + // to help debug logic errors in the program, since it failed to match + // an appropriate error handler to the error condition it encountered. + // In this program this handler will never be called. + []( leaf::error_info const & unmatched ) + { + std::cerr << + "Unknown failure detected" << std::endl << + "Cryptic diagnostic information follows" << std::endl << + unmatched; + return 6; + } ); +} + + +// Implementations of the functions called by main: + + +// Parse the command line, return the file name. +result parse_command_line( int argc, char const * argv[] ) +{ + if( argc==2 ) + return argv[1]; + else + return leaf::new_error(bad_command_line); +} + + +// Open a file for reading. +result> file_open( char const * file_name ) +{ + if( FILE * f = fopen(file_name, "rb") ) + return std::shared_ptr(f, &fclose); + else + return leaf::new_error(open_error, leaf::e_errno{errno}); +} + + +// Return the size of the file. +result file_size( FILE & f ) +{ + auto load = leaf::on_error([] { return leaf::e_errno{errno}; }); + + if( fseek(&f, 0, SEEK_END) ) + return leaf::new_error(size_error); + + int s = ftell(&f); + if( s==-1L ) + return leaf::new_error(size_error); + + if( fseek(&f,0,SEEK_SET) ) + return leaf::new_error(size_error); + + return s; +} + + +// Read size bytes from f into buf. +result file_read( FILE & f, void * buf, int size ) +{ + int n = fread(buf, 1, size, &f); + + if( ferror(&f) ) + return leaf::new_error(read_error, leaf::e_errno{errno}); + + if( n!=size ) + return leaf::new_error(eof_error); + + return { }; +} + +//////////////////////////////////////// + +#ifdef BOOST_LEAF_NO_EXCEPTIONS + +namespace boost +{ + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e ) + { + std::cerr << "Terminating due to a C++ exception under BOOST_LEAF_NO_EXCEPTIONS: " << e.what(); + std::terminate(); + } + + struct source_location; + BOOST_LEAF_NORETURN void throw_exception( std::exception const & e, boost::source_location const & ) + { + throw_exception(e); + } +} + +#endif diff --git a/src/boost/libs/leaf/example/print_file/readme.md b/src/boost/libs/leaf/example/print_file/readme.md new file mode 100644 index 000000000..3c5a51cda --- /dev/null +++ b/src/boost/libs/leaf/example/print_file/readme.md @@ -0,0 +1,16 @@ +# Print File Example + +This directory has three versions of the same simple program, which reads a +file, prints it to standard out and handles errors using LEAF, each using a +different variation on error handling: + +* [print_file_result.cpp](./print_file_result.cpp) reports errors with + `leaf::result`, using an error code `enum` for classification of failures. + +* [print_file_outcome_result.cpp](./print_file_outcome_result.cpp) is the same + as the above, but using `outcome::result`. This demonstrates the ability + to transport arbitrary error objects through APIs that do not use + `leaf::result`. + +* [print_file_eh.cpp](./print_file_eh.cpp) throws on error, using an error code + `enum` for classification of failures. -- cgit v1.2.3