summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/leaf/example/print_file
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:54:28 +0000
commite6918187568dbd01842d8d1d2c808ce16a894239 (patch)
tree64f88b554b444a49f656b6c656111a145cbbaa28 /src/boost/libs/leaf/example/print_file
parentInitial commit. (diff)
downloadceph-upstream/18.2.2.tar.xz
ceph-upstream/18.2.2.zip
Adding upstream version 18.2.2.upstream/18.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/boost/libs/leaf/example/print_file')
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_eh.cpp198
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_outcome_result.cpp233
-rw-r--r--src/boost/libs/leaf/example/print_file/print_file_result.cpp225
-rw-r--r--src/boost/libs/leaf/example/print_file/readme.md16
4 files changed, 672 insertions, 0 deletions
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 <boost/leaf.hpp>
+#include <iostream>
+#include <stdio.h>
+
+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> 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<FILE> 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<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, 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<error_code, open_error>, 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<error_code, size_error, read_error, eof_error>, 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<error_code, output_error>, 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<error_code, bad_command_line> )
+ {
+ 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> file_open( char const * file_name )
+{
+ if( FILE * f = fopen(file_name, "rb") )
+ return std::shared_ptr<FILE>(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 <boost/outcome/std_result.hpp>
+#include <boost/leaf.hpp>
+#include <iostream>
+#include <stdio.h>
+
+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 <class T>
+using result = outcome::std_result<T>;
+
+// To enable LEAF to work with outcome::result, we need to specialize the
+// is_result_type template:
+namespace boost { namespace leaf {
+ template <class T> struct is_result_type<outcome::std_result<T>>: 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<T>:
+
+// Parse the command line, return the file name.
+result<char const *> parse_command_line( int argc, char const * argv[] );
+
+// Open a file for reading.
+result<std::shared_ptr<FILE>> file_open( char const * file_name );
+
+// Return the size of the file.
+result<int> file_size( FILE & f );
+
+// Read size bytes from f into buf.
+result<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_handle_all(
+
+ [&]() -> result<int>
+ {
+ 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<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, 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<error_code, open_error>, 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<error_code, size_error, read_error, eof_error>, 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<error_code, output_error>, 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<error_code, bad_command_line> )
+ {
+ 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<char const *> 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<std::shared_ptr<FILE>> file_open( char const * file_name )
+{
+ if( FILE * f = fopen(file_name, "rb") )
+ return std::shared_ptr<FILE>(f, &fclose);
+ else
+ return leaf::new_error(open_error, leaf::e_errno{errno}).to_error_code();
+}
+
+
+// Return the size of the file.
+result<int> 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<void> 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 <boost/leaf.hpp>
+#include <iostream>
+#include <stdio.h>
+
+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 <class T>
+using result = leaf::result<T>;
+
+
+// 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<T>:
+
+// Parse the command line, return the file name.
+result<char const *> parse_command_line( int argc, char const * argv[] );
+
+// Open a file for reading.
+result<std::shared_ptr<FILE>> file_open( char const * file_name );
+
+// Return the size of the file.
+result<int> file_size( FILE & f );
+
+// Read size bytes from f into buf.
+result<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_handle_all(
+
+ [&]() -> result<int>
+ {
+ 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<error_code, open_error>, leaf::match_value<leaf::e_errno, ENOENT>, 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<error_code, open_error>, 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<error_code, size_error, read_error, eof_error>, 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<error_code, output_error>, 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<error_code, bad_command_line> )
+ {
+ 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<char const *> 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<std::shared_ptr<FILE>> file_open( char const * file_name )
+{
+ if( FILE * f = fopen(file_name, "rb") )
+ return std::shared_ptr<FILE>(f, &fclose);
+ else
+ return leaf::new_error(open_error, leaf::e_errno{errno});
+}
+
+
+// Return the size of the file.
+result<int> 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<void> 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<T>`, 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<T>`. This demonstrates the ability
+ to transport arbitrary error objects through APIs that do not use
+ `leaf::result<T>`.
+
+* [print_file_eh.cpp](./print_file_eh.cpp) throws on error, using an error code
+ `enum` for classification of failures.