factor out getopt processing
This commit is contained in:
parent
d63e62f1a7
commit
586b3244a4
@ -4,6 +4,7 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <utility>
|
||||
#include <optional>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <unistd.h>
|
||||
@ -11,54 +12,119 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#include <doctest.h>
|
||||
|
||||
auto main(int argc, char ** argv) -> int
|
||||
namespace
|
||||
{
|
||||
bool run_tests {};
|
||||
char const* in_name {};
|
||||
char const* out_name {};
|
||||
|
||||
try {
|
||||
/// @brief Fields corresponding to command line options
|
||||
struct Options
|
||||
{
|
||||
/// -i: Input file name or nullptr for stdin
|
||||
char const *in_name;
|
||||
/// -o: Output file name or nullptr for stdout
|
||||
char const *out_name;
|
||||
/// -t: Flag to run tests instead of solution
|
||||
bool run_tests;
|
||||
/// -h: Flag to print help and exit
|
||||
bool print_help;
|
||||
};
|
||||
|
||||
/// @brief Parse the basic command line options used by all solution programs
|
||||
/// @param[in,out] argc argc from main
|
||||
/// @param[in,out] argv argv from main
|
||||
/// @return options structure on success
|
||||
///
|
||||
/// `argc` and `argv` are updated to have the processed options removed from them
|
||||
/// on a successful options parse.
|
||||
auto ParseOptions(int &argc, char **&argv) -> std::optional<Options>
|
||||
{
|
||||
Options result{};
|
||||
|
||||
int ch;
|
||||
while ((ch = getopt(argc, argv, ":hi:o:t")) != -1) {
|
||||
switch (ch) {
|
||||
case 'i': in_name = optarg; break;
|
||||
case 'o': out_name = optarg; break;
|
||||
case 't': run_tests = true; break;
|
||||
case ':':
|
||||
std::cerr << "Missing argument to -" << char(optopt) << std::endl;
|
||||
return 1;
|
||||
case 'h':
|
||||
std::cerr << "Usage: " << basename(argv[0]) << " [-i INPUT] [-o OUTPUT] [-t]" << std::endl;
|
||||
return 0;
|
||||
case '?':
|
||||
default:
|
||||
std::cerr << "Unknown flag -" << char(optopt) << std::endl;
|
||||
return 1;
|
||||
while ((ch = getopt(argc, argv, ":hi:o:t")) != -1)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case 'i':
|
||||
result.in_name = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
result.out_name = optarg;
|
||||
break;
|
||||
case 't':
|
||||
result.run_tests = true;
|
||||
break;
|
||||
case ':':
|
||||
std::cerr << "Missing argument to -" << char(optopt) << std::endl;
|
||||
return {};
|
||||
case 'h':
|
||||
result.print_help = true;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
std::cerr << "Unknown flag -" << char(optopt) << std::endl;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (run_tests) {
|
||||
return doctest::Context{argc, argv}.run();
|
||||
}
|
||||
// Consume the option command line arguments to prepare for
|
||||
// additional subsequent consumers (like doctest)
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
std::ifstream fin;
|
||||
std::ofstream fout;
|
||||
std::istream & in {in_name ? fin = std::ifstream{in_name } : std::cin };
|
||||
std::ostream & out {out_name ? fout = std::ofstream{out_name} : std::cout};
|
||||
return result;
|
||||
}
|
||||
|
||||
if (in.fail()) {
|
||||
std::cerr << "Bad input file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
if (out.fail()) {
|
||||
std::cerr << "Bad output file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
auto main(int argc, char **argv) -> int
|
||||
{
|
||||
Options options;
|
||||
if (auto o = ParseOptions(argc, argv))
|
||||
{
|
||||
options = *o;
|
||||
}
|
||||
else
|
||||
{
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (options.print_help)
|
||||
{
|
||||
std::cerr << "Usage: " << basename(argv[0]) << " [-i INPUT] [-o OUTPUT] [-t]" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (options.run_tests)
|
||||
{
|
||||
return doctest::Context{argc, argv}.run();
|
||||
}
|
||||
|
||||
// Establish input streams
|
||||
std::ifstream fin;
|
||||
std::ofstream fout;
|
||||
std::istream &in{options.in_name ? fin = std::ifstream{options.in_name} : std::cin};
|
||||
std::ostream &out{options.out_name ? fout = std::ofstream{options.out_name} : std::cout};
|
||||
|
||||
if (in.fail())
|
||||
{
|
||||
std::cerr << "Bad input file" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (out.fail())
|
||||
{
|
||||
std::cerr << "Bad output file" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Main(in, out);
|
||||
} catch (std::exception const& e) {
|
||||
std::cerr << "Program failed: " << e.what() << std::endl;
|
||||
return 1;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
catch (std::exception const &e)
|
||||
{
|
||||
std::cerr << "Solution failed: " << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user