// MIT License // // Copyright (c) 2020, The Regents of the University of California, // through Lawrence Berkeley National Laboratory (subject to receipt of any // required approvals from the U.S. Dept. of Energy). All rights reserved. // // 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. #pragma once #include "timemory/utility/macros.hpp" #include "timemory/utility/types.hpp" #include "timemory/utility/utility.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace tim { namespace argparse { namespace helpers { // //--------------------------------------------------------------------------------------// // static inline bool not_is_space(int ch) { return std::isspace(ch) == 0; } // //--------------------------------------------------------------------------------------// // static inline uint64_t lcount(const std::string& s, bool (*f)(int) = not_is_space) { uint64_t c = 0; for(size_t i = 0; i < s.length(); ++i, ++c) { if(f(s.at(i))) break; } return c; } // //--------------------------------------------------------------------------------------// // static inline std::string ltrim(std::string s, bool (*f)(int) = not_is_space) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), f)); return s; } // //--------------------------------------------------------------------------------------// // static inline std::string rtrim(std::string s, bool (*f)(int) = not_is_space) { s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end()); return s; } // //--------------------------------------------------------------------------------------// // static inline std::string trim(std::string s, bool (*f)(int) = not_is_space) { ltrim(s, f); rtrim(s, f); return s; } // //--------------------------------------------------------------------------------------// // static inline char* strdup(const char* s) { auto slen = strlen(s); auto* result = new char[slen + 1]; if(result) { memcpy(result, s, slen * sizeof(char)); result[slen] = '\0'; return result; } return nullptr; } // //--------------------------------------------------------------------------------------// // template static inline std::string join(InputIt begin, InputIt end, const std::string& separator = " ") { std::ostringstream ss; if(begin != end) { ss << *begin++; } while(begin != end) { ss << separator; ss << *begin++; } return ss.str(); } // //--------------------------------------------------------------------------------------// // static inline bool is_numeric(const std::string& arg) { auto _nidx = arg.find_first_of("0123456789"); auto _oidx = arg.find_first_not_of("0123456789.Ee+-*/"); // must have number somewhere if(_nidx == std::string::npos) return false; // if something other than number or scientific notation if(_oidx != std::string::npos) return false; // numbers + possible scientific notation return true; } // //--------------------------------------------------------------------------------------// // static inline int find_equiv(const std::string& s) { for(size_t i = 0; i < s.length(); ++i) { // if find graph symbol before equal, end search // i.e. don't accept --asd)f=0 arguments // but allow --asd_f and --asd-f arguments if(std::ispunct(static_cast(s[i])) != 0) { if(s[i] == '=') { return static_cast(i); } if(s[i] == '_' || s[i] == '-') { continue; } return -1; } } return -1; } // //--------------------------------------------------------------------------------------// // static inline size_t find_punct(const std::string& s) { size_t i; for(i = 0; i < s.length(); ++i) { if((std::ispunct(static_cast(s[i])) != 0) && s[i] != '-') { break; } } return i; } // //--------------------------------------------------------------------------------------// // namespace is_container_impl { // template struct is_container : std::false_type {}; // template struct is_container> : std::true_type {}; template struct is_container> : std::true_type {}; template struct is_container> : std::true_type {}; template struct is_container> : std::true_type {}; // template struct is_initializing_container : is_container::type {}; // template struct is_initializing_container> : std::true_type {}; } // namespace is_container_impl // //--------------------------------------------------------------------------------------// // // type trait to utilize the implementation type traits as well as decay the type template struct is_container { static constexpr bool const value = is_container_impl::is_container>::value; }; // //--------------------------------------------------------------------------------------// // // type trait to utilize the implementation type traits as well as decay the type template struct is_initializing_container { static constexpr bool const value = is_container_impl::is_initializing_container>::value; }; // //--------------------------------------------------------------------------------------// // } // namespace helpers // //--------------------------------------------------------------------------------------// // // argument vector // //--------------------------------------------------------------------------------------// // /// \struct tim::argparse::argument_vector /// \brief This class exists to simplify creating argument arrays compatible with execv* /// routines and MPI_Comm_spawn/MPI_Comm_spawn_multiple /// struct argument_vector : std::vector { struct c_args : std::tuple { using base_type = std::tuple; template c_args(Args&&... args) : base_type(std::forward(args)...) {} auto& argc() { return std::get<0>(*this); } auto& argv() { return std::get<1>(*this); } auto& args() { return std::get<2>(*this); } TIMEMORY_NODISCARD const auto& argc() const { return std::get<0>(*this); } TIMEMORY_NODISCARD const auto& argv() const { return std::get<1>(*this); } TIMEMORY_NODISCARD const auto& args() const { return std::get<2>(*this); } void clear() { // uses comma operator to execute delete and return nullptr for(int i = 0; i < argc(); ++i) argv()[i] = (delete[] argv()[i], nullptr); argv() = (delete[] argv(), nullptr); } }; using base_type = std::vector; using cargs_t = c_args; template argument_vector(Args&&... args) : base_type(std::forward(args)...) {} explicit argument_vector(int& argc, char**& argv); explicit argument_vector(int& argc, const char**& argv); explicit argument_vector(int& argc, const char* const*& argv); TIMEMORY_NODISCARD cargs_t get_execv(const base_type& _prepend, size_t _beg = 0, size_t _end = std::numeric_limits::max()) const; TIMEMORY_NODISCARD cargs_t get_execv(size_t _beg = 0, size_t _end = std::numeric_limits::max()) const; // helper function to free the memory created by get_execv, pass by reference // so that we can set values to nullptr and avoid multiple delete errors static void free_execv(cargs_t& itr) { itr.clear(); } }; // //--------------------------------------------------------------------------------------// // // argument parser // //--------------------------------------------------------------------------------------// // struct argument_parser { struct arg_result; using this_type = argument_parser; using result_type = arg_result; using bool_func_t = std::function; using action_func_t = std::function; using action_pair_t = std::pair; using error_func_t = std::function; using known_args_t = std::tuple; using strvec_t = std::vector; using strset_t = std::set; // //----------------------------------------------------------------------------------// // struct arg_result { arg_result() = default; arg_result(std::string err) noexcept : m_error(true) , m_what(std::move(err)) {} operator bool() const { return m_error; } friend std::ostream& operator<<(std::ostream& os, const arg_result& dt); TIMEMORY_NODISCARD const std::string& what() const { return m_what; } private: bool m_error = false; std::string m_what = {}; }; // //----------------------------------------------------------------------------------// // struct argument { using callback_t = std::function; enum Position : int { LastArgument = -1, IgnoreArgument = -2 }; enum Count : int { ANY = -1 }; ~argument() { m_destroy(m_default); } argument& name(const std::string& name) { m_names.push_back(name); return *this; } argument& names(const std::vector& names) { for(const auto& itr : names) m_names.push_back(itr); return *this; } argument& description(const std::string& description) { m_desc = description; return *this; } argument& dtype(const std::string& _dtype) { m_dtype = _dtype; return *this; } argument& required(bool req) { m_required = req; return *this; } argument& position(int position) { if(position != Position::LastArgument) { // position + 1 because technically argument zero is the name of the // executable m_position = position + 1; } else { m_position = position; } return *this; } argument& max_count(int count) { m_max_count = count; return *this; } argument& min_count(int count) { m_min_count = count; return *this; } argument& count(int count) { m_count = count; return *this; } template argument& set_default(const T& val) { m_found = true; m_default_tidx = std::type_index{ typeid(decay_t) }; m_callback = [&](void*& obj) { m_destroy(obj); if(!obj) obj = (void*) new T{}; (*static_cast(obj)) = val; }; m_destroy = [](void*& obj) { if(obj) delete static_cast(obj); }; return *this; } template argument& set_default(T& val) { m_found = true; m_default_tidx = std::type_index{ typeid(decay_t) }; m_callback = [&](void*& obj) { obj = (void*) &val; }; return *this; } template argument& choices(const std::initializer_list& _choices) { for(auto&& itr : _choices) { std::stringstream ss; ss << itr; m_choices.insert(ss.str()); } return *this; } template