| @@ -0,0 +1,2 @@ | |||||
| build/ | |||||
| .vscode/ | |||||
| @@ -0,0 +1,3 @@ | |||||
| [submodule "cmake/modules"] | |||||
| path = cmake/modules | |||||
| url = b3rgmann@git.bergmann89.de:cpp/CmakeModules.git | |||||
| @@ -0,0 +1,15 @@ | |||||
| # Initialize CMake ################################################################################ | |||||
| CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR ) | |||||
| Include ( CTest ) | |||||
| If ( NOT CMAKE_BUILD_TYPE ) | |||||
| Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) | |||||
| EndIf ( NOT CMAKE_BUILD_TYPE ) | |||||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" | |||||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) | |||||
| # Projects ######################################################################################## | |||||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) | |||||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||||
| @@ -0,0 +1,21 @@ | |||||
| MIT License | |||||
| Copyright (c) 2018 Erik Junghanns (aka Bergmann89) | |||||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| of this software and associated documentation files (the "Software"), to deal | |||||
| in the Software without restriction, including without limitation the rights | |||||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| copies of the Software, and to permit persons to whom the Software is | |||||
| furnished to do so, subject to the following conditions: | |||||
| The above copyright notice and this permission notice shall be included in all | |||||
| copies or substantial portions of the Software. | |||||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||||
| SOFTWARE. | |||||
| @@ -0,0 +1 @@ | |||||
| Subproject commit 1e74005bc2f91434fecb2e5f698c21b73f7e3a13 | |||||
| @@ -0,0 +1,6 @@ | |||||
| #pragma once | |||||
| #include "cppargs/option.h" | |||||
| #include "cppargs/parser.h" | |||||
| #include "cppargs/option.inl" | |||||
| @@ -0,0 +1,161 @@ | |||||
| #pragma once | |||||
| #include <string> | |||||
| namespace cppargs | |||||
| { | |||||
| enum class next_result | |||||
| { | |||||
| finished = 0, //!< context does not have any further arguments | |||||
| option_short = 1, //!< moving to next argument was successfull (argument starts with - or --) | |||||
| option_long = 2, //!< moving to next argument was successfull (argument starts with - or --) | |||||
| argument = 3, //!< moving to next argument was successfull (argument does not starts with - or --) | |||||
| }; | |||||
| struct context | |||||
| { | |||||
| int const argc; | |||||
| char const * const * const argv; | |||||
| char const * const exe; | |||||
| char const * arg { nullptr }; | |||||
| char const * value { nullptr }; | |||||
| int index { 0 }; | |||||
| inline next_result next(std::string* key = nullptr) | |||||
| { | |||||
| if (key) | |||||
| key->clear(); | |||||
| if (index + 1 >= argc) | |||||
| return next_result::finished; | |||||
| ++index; | |||||
| arg = argv[index]; | |||||
| value = nullptr; | |||||
| /* check argument */ | |||||
| int i = 0; | |||||
| value = nullptr; | |||||
| while (arg[i] == '-') | |||||
| ++i; | |||||
| /* parse value */ | |||||
| if (i == 1) | |||||
| { | |||||
| if (key) | |||||
| *key = std::string("-") + arg[1]; | |||||
| if (arg[2] != '\0') | |||||
| value = &arg[2]; | |||||
| return next_result::option_short; | |||||
| } | |||||
| else if (i == 2) | |||||
| { | |||||
| auto tmp = strchr(arg, '='); | |||||
| if (tmp) | |||||
| { | |||||
| value = tmp + 1; | |||||
| if (key) | |||||
| *key = std::string(arg, static_cast<size_t>(tmp - arg - 1)); | |||||
| } | |||||
| else if (key) | |||||
| { | |||||
| *key = arg; | |||||
| } | |||||
| return next_result::option_long; | |||||
| } | |||||
| else | |||||
| { | |||||
| return next_result::argument; | |||||
| } | |||||
| } | |||||
| }; | |||||
| struct option | |||||
| { | |||||
| public: | |||||
| const std::string long_name; | |||||
| const char short_name; | |||||
| public: | |||||
| inline option( | |||||
| const std::string& p_long_name, | |||||
| const char p_short_name); | |||||
| virtual ~option() = default; | |||||
| virtual void parse(context& c) = 0; | |||||
| }; | |||||
| template<typename T_predicate> | |||||
| struct predicate_option | |||||
| : public option | |||||
| { | |||||
| public: | |||||
| using predicate_type = T_predicate; | |||||
| private: | |||||
| predicate_type _predicate; | |||||
| public: | |||||
| template<typename... T_args> | |||||
| inline predicate_option( | |||||
| const std::string& p_long_name, | |||||
| const char p_short_name, | |||||
| T_args&&... args); | |||||
| virtual void parse(context& c) override; | |||||
| }; | |||||
| template<typename T_value> | |||||
| struct reference_option | |||||
| : public option | |||||
| { | |||||
| public: | |||||
| using value_type = T_value; | |||||
| private: | |||||
| value_type& _value; | |||||
| public: | |||||
| inline reference_option( | |||||
| const std::string& p_long_name, | |||||
| const char p_short_name, | |||||
| value_type& p_value) | |||||
| : option(p_long_name, p_short_name) | |||||
| , _value(p_value) | |||||
| { } | |||||
| virtual void parse(context& c) override | |||||
| { | |||||
| auto old = c; | |||||
| auto value = c.value; | |||||
| if (!value) | |||||
| { | |||||
| auto ret = c.next(); | |||||
| if (ret != next_result::argument) | |||||
| throw std::runtime_error(std::string("option ") + old.arg + " expected one argument"); | |||||
| value = c.arg; | |||||
| } | |||||
| std::cout << value << std::endl; | |||||
| } | |||||
| }; | |||||
| template<typename T_predicate> | |||||
| constexpr decltype(auto) make_predicate_option(const std::string& long_name, char short_name, T_predicate&& predicate) | |||||
| { | |||||
| using predicate_type = T_predicate; | |||||
| using predicate_option_type = predicate_option<predicate_type>; | |||||
| return std::make_unique<predicate_option_type>(long_name, short_name, std::forward<predicate_type>(predicate)); | |||||
| } | |||||
| template<typename T_value> | |||||
| constexpr decltype(auto) make_reference_option(const std::string& long_name, char short_name, T_value& value) | |||||
| { | |||||
| using value_type = T_value; | |||||
| using reference_option_type = reference_option<value_type>; | |||||
| return std::make_unique<reference_option_type>(long_name, short_name, value); | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,33 @@ | |||||
| #pragma once | |||||
| #include "option.h" | |||||
| namespace cppargs | |||||
| { | |||||
| /* option */ | |||||
| option::option( | |||||
| const std::string& p_long_name, | |||||
| const char p_short_name) | |||||
| : long_name (p_long_name) | |||||
| , short_name(p_short_name) | |||||
| { } | |||||
| /* predicate_option */ | |||||
| template<typename T_predicate> | |||||
| template<typename... T_args> | |||||
| predicate_option<T_predicate>::predicate_option( | |||||
| const std::string& p_long_name, | |||||
| const char p_short_name, | |||||
| T_args&&... args) | |||||
| : option(p_long_name, p_short_name) | |||||
| , _predicate(std::forward<T_args>(args)...) | |||||
| { } | |||||
| template<typename T_predicate> | |||||
| void predicate_option<T_predicate>::parse(context& c) | |||||
| { _predicate(c); } | |||||
| } | |||||
| @@ -0,0 +1,96 @@ | |||||
| #pragma once | |||||
| #include <map> | |||||
| #include <vector> | |||||
| #include <memory> | |||||
| #include <cstring> | |||||
| #include "option.h" | |||||
| namespace cppargs | |||||
| { | |||||
| struct parser | |||||
| { | |||||
| private: | |||||
| using option_ptr_type = std::unique_ptr<option>; | |||||
| using option_vector_type = std::vector<option_ptr_type>; | |||||
| using option_map_type = std::map<std::string, option*>; | |||||
| private: | |||||
| option_ptr_type _default; | |||||
| option_vector_type _options; | |||||
| option_map_type _map; | |||||
| inline void update_map() | |||||
| { | |||||
| _map.clear(); | |||||
| for (auto& o : _options) | |||||
| { | |||||
| if (!o) | |||||
| throw std::runtime_error("expectd option to not be null!"); | |||||
| if (o->short_name) | |||||
| _map.emplace(std::string("-") + o->short_name, o.get()); | |||||
| if (!o->long_name.empty()) | |||||
| _map.emplace(std::string("--") + o->long_name, o.get()); | |||||
| } | |||||
| } | |||||
| inline void handle_invalid_arg(context& c) const | |||||
| { | |||||
| if (_default) | |||||
| { | |||||
| _default->parse(c); | |||||
| } | |||||
| else | |||||
| { | |||||
| throw std::runtime_error(std::string("invalid or unknown argument: ") + c.arg); | |||||
| } | |||||
| } | |||||
| public: | |||||
| template<typename... T_option> | |||||
| inline parser(option_ptr_type&& default_option, T_option&&... option) | |||||
| : _default(std::move(default_option)) | |||||
| { | |||||
| (void) (int[]) { 0, (_options.emplace_back(std::forward<T_option>(option)), 0)... }; | |||||
| update_map(); | |||||
| } | |||||
| void parse(int argc, char const * argv[]) const | |||||
| { | |||||
| if (argc <= 0) { | |||||
| return; | |||||
| } | |||||
| context c | |||||
| { | |||||
| argc, | |||||
| argv, | |||||
| argv[0], | |||||
| }; | |||||
| next_result ret; | |||||
| std::string key; | |||||
| while ((ret = c.next(&key)) != next_result::finished) | |||||
| { | |||||
| /* move to next option */ | |||||
| if ( ret != next_result::option_short | |||||
| && ret != next_result::option_long) | |||||
| handle_invalid_arg(c); | |||||
| /* find option */ | |||||
| auto it = _map.find(key); | |||||
| if (it == _map.end()) | |||||
| { | |||||
| handle_invalid_arg(c); | |||||
| } | |||||
| else | |||||
| { | |||||
| it->second->parse(c); | |||||
| } | |||||
| } | |||||
| } | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,24 @@ | |||||
| # Initialize ###################################################################################### | |||||
| Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||||
| Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | |||||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||||
| # Project: cppargs ################################################################################ | |||||
| Project ( cppargs VERSION 1.0.0.0 LANGUAGES CXX ) | |||||
| Set ( CPPARGS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | |||||
| Add_Library ( cppargs INTERFACE ) | |||||
| Target_Include_Directories ( cppargs | |||||
| INTERFACE ${CPPARGS_INCLUDE_DIR} ) | |||||
| If ( HAS_PEDANTIC ) | |||||
| Pedantic_Apply_Flags ( ALL ) | |||||
| EndIf ( ) | |||||
| # Optimize | |||||
| If ( __COTIRE_INCLUDED ) | |||||
| Cotire ( cppargs ) | |||||
| EndIf ( ) | |||||
| @@ -0,0 +1,35 @@ | |||||
| # Initialize ###################################################################################### | |||||
| Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) | |||||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | |||||
| Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | |||||
| Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) | |||||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||||
| # Project: cppargs-test ########################################################################### | |||||
| Find_Package ( GTest REQUIRED ) | |||||
| Project ( cppargs-test ) | |||||
| File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||||
| Add_Executable ( cppargs-test EXCLUDE_FROM_ALL ${SOURCE_FILES} ) | |||||
| Target_Link_Libraries ( cppargs-test | |||||
| cppargs | |||||
| GTest::Main ) | |||||
| If ( HAS_PEDANTIC ) | |||||
| Pedantic_Apply_Flags ( ALL ) | |||||
| EndIf ( ) | |||||
| # optimization | |||||
| If ( HAS_COTIRE ) | |||||
| Cotire ( cppargs-test ) | |||||
| EndIf ( ) | |||||
| # test | |||||
| If ( HAS_CMAKE_TESTS ) | |||||
| Add_CMake_Test ( NAME cppargs TARGET cppargs-test ) | |||||
| Else ( ) | |||||
| Add_Test ( NAME cppargs COMMAND cppargs-test ) | |||||
| EndIf ( ) | |||||
| @@ -0,0 +1,22 @@ | |||||
| #include <gtest/gtest.h> | |||||
| #include <cppargs.h> | |||||
| TEST(cppargs, simple) | |||||
| { | |||||
| int x; | |||||
| cppargs::parser p( | |||||
| nullptr, | |||||
| cppargs::make_reference_option("index", 'i', x), | |||||
| cppargs::make_predicate_option("test", 't', [](auto& c){ | |||||
| std::cout << c.arg << std::endl; | |||||
| }) | |||||
| ); | |||||
| std::vector<const char*> args({ | |||||
| "/fuu/bar/exe", | |||||
| "-i", "6", | |||||
| "--test" | |||||
| }); | |||||
| p.parse(static_cast<int>(args.size()), args.data()); | |||||
| } | |||||