From 291c23124fa5199590501160e2760862eb5d1e70 Mon Sep 17 00:00:00 2001 From: NaiJi Date: Thu, 25 Mar 2021 15:27:50 +0300 Subject: [PATCH] Clean arguments parsing --- CMakeLists.txt | 25 ++++----- argsprocessor.cpp | 136 ++++++++++++++++++++++++++++++++++++++++++++++ argsprocessor.h | 29 ++++++++++ main.cpp | 108 +++--------------------------------- 4 files changed, 185 insertions(+), 113 deletions(-) create mode 100644 argsprocessor.cpp create mode 100644 argsprocessor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 071a8f6..103f5e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,22 +4,21 @@ project(sliding-puzzle LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(SOURCES application.cpp board.cpp main.cpp) -set(HEADER_FILES application.h board.h filepath_util.h output_util.h) -add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES}) +set(SOURCES application.cpp board.cpp main.cpp argsprocessor.cpp) +set(HEADER_FILES application.h board.h filepath_util.h output_util.h argsprocessor.h) # STATIC # # You need to build SFML from sources with cmake -#set(SFML_LIB_DIR -# ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-graphics.so.2.5 -# ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-system.so.2.5 -# ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-window.so.2.5) -#set(SFML_INCL_DIR ${CMAKE_SOURCE_DIR}/SFML-2.5.1/include) -#include_directories(${SFML_INCL_DIR}) -#add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES} ) -#target_link_libraries(sliding-puzzle ${SFML_LIB_DIR}) +set(SFML_LIB_DIR + ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-graphics.so.2.5 + ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-system.so.2.5 + ${CMAKE_SOURCE_DIR}/SFML-2.5.1/lib/libsfml-window.so.2.5) +set(SFML_INCL_DIR ${CMAKE_SOURCE_DIR}/SFML-2.5.1/include) +include_directories(${SFML_INCL_DIR}) +add_executable(sliding-puzzle ${SOURCES} ${HEADER_FILES} ) +target_link_libraries(sliding-puzzle ${SFML_LIB_DIR}) # DYNAMIC # # You only need to install SFML from your package manager -find_package(SFML REQUIRED graphics window system) -target_link_libraries(sliding-puzzle sfml-system sfml-graphics sfml-network) +#find_package(SFML REQUIRED graphics window system) +#target_link_libraries(sliding-puzzle sfml-system sfml-graphics sfml-network) diff --git a/argsprocessor.cpp b/argsprocessor.cpp new file mode 100644 index 0000000..f7990a4 --- /dev/null +++ b/argsprocessor.cpp @@ -0,0 +1,136 @@ +#include "argsprocessor.h" +#include "output_util.h" +#include "filepath_util.h" + +#include +#include + +///////////////////////////////////////////////////////////////////////// + +static constexpr int DEFAULT_SCREEN_WIDTH = 1280; +static constexpr int DEFAULT_SCREEN_HEIGHT = 720; +static constexpr int DEFAULT_SPLITTING = 4; +static const std::string DEFAULT_PATH = "."; + +///////////////////////////////////////////////////////////////////////// + +ArgsProcessor::ArgsProcessor(int argc, char **argv) +{ + parse_result = tryConvertInput(argc, argv); +} + +int ArgsProcessor::broken() const +{ + return parse_result; +} + +std::tuple ArgsProcessor::unpack() const +{ + return {image_splitting, game_resolution, image_path}; +} + +int ArgsProcessor::tryConvertInput(int argc, char **argv) +{ + int error = iterateArgc(argc, argv); + if (error) + return error; + + if (image_path == DEFAULT_PATH) + { + // no path was given, loading random image from '.' + const auto &[error, ret_path] = filepath::parsePath(image_path); + if (error) + return makeError(output::IMG_FAIL_MSG); + + image_path = ret_path; + } + + return EXIT_SUCCESS; +} + +bool ArgsProcessor::isFlag(const char *arg, const char *flag) const +{ + return (strcmp(arg, flag) == 0); +} + +int ArgsProcessor::iterateArgc(int argc, char **argv) +{ + for (int current_arg = 1; current_arg < argc; ++current_arg) // current_arg = 0 is executable name + { + if (isFlag(argv[current_arg], output::HELP_FLAG)) + return makeError(output::HELP_MSG); + + if (isFlag(argv[current_arg], output::SPLITTING_FLAG)) + { + int error = parseSplitting(current_arg, argc, argv); + if (error) + return error; + + ++current_arg; + continue; + } + + if (isFlag(argv[current_arg], output::RESOLUTION_FLAG)) + { + int error = parseResolution(current_arg, argc, argv); + if (error) + return error; + + ++current_arg; + continue; + } + + const auto &[error, ret_path] = filepath::parsePath(argv[current_arg]); + + if (error) + return makeError(output::IMG_FAIL_MSG); + + image_path = ret_path; + } + + return EXIT_SUCCESS; +} + +int ArgsProcessor::parseSplitting(int curr_arg, int argc, char **argv) +{ + const int value_rg = curr_arg + 1; + if (value_rg == argc) + return makeError(output::SPLITTING_MSG); + + image_splitting = -1; + if (std::isdigit(*argv[value_rg])) + image_splitting = std::stoi(argv[value_rg]); + + if (image_splitting < 2) + return makeError(output::SPLITTING_MSG); + + return EXIT_SUCCESS; +} + +int ArgsProcessor::parseResolution(int curr_arg, int argc, char **argv) +{ + const int value_rg = curr_arg + 1; + if (value_rg == argc) + return makeError(output::RESOLUTION_MSG); + + std::vector res = filepath::split(argv[value_rg], 'x'); // splitting argument by 'x' as in 600x900 + + if (res.size() < 2) + return makeError(output::RESOLUTION_MSG); + + game_resolution = {-1, -1}; + + if (std::isdigit(*res[0].c_str()) && std::isdigit(*res[1].c_str())) + game_resolution = {std::stoi(res[0].c_str()), std::stoi(res[1].c_str())}; + + if (game_resolution.x < 1 || game_resolution.y < 1) + return makeError(output::RESOLUTION_MSG); + + return EXIT_SUCCESS; +} + +int ArgsProcessor::makeError(const char* msg) const +{ + std::cout << msg; + return EXIT_FAILURE; +} diff --git a/argsprocessor.h b/argsprocessor.h new file mode 100644 index 0000000..6d16e53 --- /dev/null +++ b/argsprocessor.h @@ -0,0 +1,29 @@ +#ifndef ARGSPROCESSOR_H +#define ARGSPROCESSOR_H + +#include +#include + +class ArgsProcessor +{ +public: + ArgsProcessor(int argc, char **argv); + + int broken() const; + std::tuple unpack() const; + +private: + bool isFlag(const char* arg, const char* flag) const; + int tryConvertInput(int argc, char **argv); + int iterateArgc(int argc, char **argv); + int makeError(const char* msg) const; + int parseSplitting(int curr_arg, int argc, char **argv); + int parseResolution(int curr_arg, int argc, char **argv); + + int parse_result; + int image_splitting; + sf::Vector2i game_resolution; + std::string image_path; +}; + +#endif // ARGSPROCESSOR_H diff --git a/main.cpp b/main.cpp index 25e6271..f8d5c0e 100644 --- a/main.cpp +++ b/main.cpp @@ -1,106 +1,14 @@ #include "application.h" -#include "output_util.h" -#include "filepath_util.h" -#include -#include - -///////////////////////////////////////////////////////////////////////// - -static constexpr int DEFAULT_SCREEN_WIDTH = 1280; -static constexpr int DEFAULT_SCREEN_HEIGHT = 720; -static constexpr int DEFAULT_SPLITTING = 4; -static const std::string DEFAULT_PATH = "."; // current folder, I guess - -///////////////////////////////////////////////////////////////////////// - -std::tuple error(const char* msg) -{ - std::cout << msg; - return {EXIT_FAILURE, -1, {}, {}}; -} - -std::tuple parseInput(int argc, char **argv) -{ - int splitting = DEFAULT_SPLITTING; - sf::Vector2i resolution(DEFAULT_SCREEN_WIDTH, DEFAULT_SCREEN_HEIGHT); - std::string path = DEFAULT_PATH; - - for (int current_arg = 1; current_arg < argc; ++current_arg) // current_arg = 0 is executable name - { - if (strcmp(argv[current_arg], output::HELP_FLAG) == 0) // --help - return error(output::HELP_MSG); - - if (strcmp(argv[current_arg], output::SPLITTING_FLAG) == 0) // -s num - { - const int value_rg = current_arg + 1; - if (value_rg == argc) // is '-s' is the last argument - return error(output::SPLITTING_MSG); - - splitting = -1; // here assuming user is providing it on their own - if (std::isdigit(*argv[value_rg])) - splitting = std::stoi(argv[value_rg]); - - if (splitting < 2) - return error(output::SPLITTING_MSG); - - ++current_arg; // skipping value after flag to not check it once again - continue; - } - - if (strcmp(argv[current_arg], output::RESOLUTION_FLAG) == 0) // -r numxnum - { - const int value_rg = current_arg + 1; - if (value_rg == argc) // is '-s' is the last argument - return error(output::RESOLUTION_MSG); - - std::vector res = filepath::split(argv[value_rg], 'x'); // splitting argument by 'x' as in 600x900 - - if (res.size() < 2) - return error(output::RESOLUTION_MSG); - - resolution = {-1, -1}; - - if (std::isdigit(*res[0].c_str()) && std::isdigit(*res[1].c_str())) - resolution = {std::stoi(res[0].c_str()), std::stoi(res[1].c_str())}; - - if (resolution.x < 1 || resolution.y < 1) - return error(output::RESOLUTION_MSG); - - ++current_arg; // skipping value after flag to not check it once again - continue; - } - - // nothing else, then assuming it's filepath or folderpath - const auto &[ret_code, ret_path] = filepath::parsePath(argv[current_arg]); - - if (ret_code) - return error(output::IMG_FAIL_MSG); - - path = ret_path; - } - - if (path == DEFAULT_PATH) - { - // no path was give, loading random image from '.' - const auto &[ret_code, ret_path] = filepath::parsePath(path); - - if (ret_code) - return error(output::IMG_FAIL_MSG); - - path = ret_path; - } - - return {EXIT_SUCCESS, splitting, resolution, path}; -} - -///////////////////////////////////////////////////////////////////////// +#include "argsprocessor.h" int main(int argc, char **argv) { - const auto&[ret_code, splitting, resolution, path] = parseInput(argc, argv); + ArgsProcessor args(argc, argv); - if (ret_code) - return ret_code; + if (args.broken()) + return EXIT_FAILURE; + + const auto&[splitting, resolution, path] = args.unpack(); Application app(resolution.x, resolution.y); if (app.init(path, splitting)) @@ -108,6 +16,6 @@ int main(int argc, char **argv) app.run(); return EXIT_SUCCESS; } - - return EXIT_FAILURE; + else + return EXIT_FAILURE; }