From 5b91fd4da3b3a7eafa76159741e67deb6c1c39c9 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 25 Sep 2019 12:56:57 +0200 Subject: [PATCH] No longer depend on Boost.program_options. --- 2019/CMakeLists.txt | 3 +- 2019/README.md | 1 - 2019/src/runner.cpp | 99 +++++++++++++++++++++++++++++++++------------ 3 files changed, 74 insertions(+), 29 deletions(-) diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 5dea4ae..a679e78 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.12) project(aoc2019) -find_package(Boost REQUIRED COMPONENTS program_options) find_package(GTest REQUIRED) add_library(AoCSolutions src/implementations.hpp src/implementations.cpp src/day01.cpp src/days.hpp) @@ -10,7 +9,7 @@ target_compile_features(AoCSolutions PUBLIC cxx_std_17) add_executable(runner src/runner.cpp) target_compile_features(runner PUBLIC cxx_std_17) -target_link_libraries(runner AoCSolutions Boost::program_options) +target_link_libraries(runner AoCSolutions) add_executable(unit_tests tests/test_solutions.cpp) target_compile_features(unit_tests PUBLIC cxx_std_17) diff --git a/2019/README.md b/2019/README.md index 160afe3..f857611 100644 --- a/2019/README.md +++ b/2019/README.md @@ -14,7 +14,6 @@ Install the dependencies: by default tries to dynamically link GTest, and the Ubuntu packages only provide a statically linked archive. You may need to compile it for yourself. -- [Boost.Program_options](https://www.boost.org/doc/libs/1_71_0/doc/html/program_options.html) ``` mkdir build && cd build diff --git a/2019/src/runner.cpp b/2019/src/runner.cpp index d47951c..c3f6b5a 100644 --- a/2019/src/runner.cpp +++ b/2019/src/runner.cpp @@ -1,45 +1,92 @@ #include "implementations.hpp" +#include #include #include -#include - -namespace po = boost::program_options; struct AoCOptions { aoc2019::solution_t implementation; bool run_timer; }; -static AoCOptions parse_options(const int argc, const char *argv[]) { +static AoCOptions parse_options(const int argc, const char* argv[]) { + using namespace std::string_view_literals; AoCOptions options{}; - int day; - bool part2; - po::options_description desc("Allowed options"); - desc.add_options() - ("day", po::value(&day)->required(), "The day to run.") - ("part2,2", po::bool_switch(&part2), "Whether to run part 2.") - ("timer,t", po::bool_switch(&options.run_timer), "Show the execution time."); - po::positional_options_description positionals; - positionals.add("day", 1); + auto show_help = [argv] (int exit_status = 0) { + std::cerr << "Usage: " << argv[0] << " [--timer|-t] [--part2|-2] [--help|-h] DAY\n" + << "\t--timer|-t: print execution time\n" + << "\t--part2|-2: run part 2\n" + << "\t --help|-h: show this message\n"; + std::exit(exit_status); + }; - try { - po::variables_map vm; + int day = -1; + bool part2 = false; - po::store(po::command_line_parser(argc, argv).options(desc).positional(positionals).run(), vm); - po::notify(vm); + // Here follows a manual implementation of getopt, since getopt doesn't work on windows… + for (int i = 1; i < argc; ++i) { + std::string_view arg(argv[i]); + if (arg[0] == '-') { + // Handle flag arguments + if (arg[1] != '-') { + // Shorthand flags + for (char c : arg.substr(1)) { + switch (c) { + case '2': + part2 = true; + break; - options.implementation = aoc2019::get_implementation(day, part2); + case 't': + options.run_timer = true; + break; - return options; - } catch (po::error &argument_error) { - std::cerr << argument_error.what() << std::endl; - std::exit(1); - } catch (std::out_of_range &) { - std::cerr << "Invalid day: " << day - << ".\n Valid range: [1, 25].\n"; - std::exit(1); + case 'h': + show_help(); + break; + + default: + std::cerr << "Unknown flag '" << c << "'.\n\n"; + show_help(1); + } + } + } else { + // Handle long form versions + if (arg == "--timer"sv) { + part2 = true; + } else if (arg == "--timer"sv) { + options.run_timer = true; + } else if (arg == "--help"sv) { + show_help(); + } else { + show_help(1); + } + } + } else { + if (day != -1) { + // Double date specification, bail. + show_help(1); + } + + // Try to parse the date number + if (auto res = std::from_chars(arg.data(), arg.data() + arg.size(), day); res.ec != std::errc()) { + auto error_code = std::make_error_code(res.ec); + std::cerr << error_code.message() << "\n\n"; + show_help(1); + } + } } + + if (day == -1) { + std::cerr << "Argument DAY is required.\n\n"; + show_help(1); + } else if (day < 1 || day > 25) { + std::cerr << "Invalid day. Valid range: [1, 25]\n"; + show_help(1); + } + + options.implementation = aoc2019::get_implementation(day, part2); + + return options; } int main(int argc, const char *argv[]) {