/* Argument Parser, supports Unicode */ #ifndef _ARGZ_HPP_X #define _ARGZ_HPP_X #include <algorithm> #include <iomanip> #include <iostream> #include <string> #include <type_traits> #include <unordered_map> #include <utility> #include <vector> template <typename T> concept StringType = std::is_class_v<T> && requires(T type) { type.length(); type[0]; type += type; type = type; typename T::value_type; typename T::size_type; { type.length() } -> std::same_as<typename T::size_type>; { type[0] } -> std::same_as<typename T::value_type &>; { type += T{} } -> std::same_as<T &>; { type = T{} } -> std::same_as<T &>; }; enum class ArgType { ARG_SINGLE, ARG_SINGLE_VALUE, ARG_DOUBLE, ARG_DOUBLE_VALUE, ARG_NONE }; template <StringType String> struct Argument { String arg_name; int arg_letter; String arg_value; ArgType arg_type; String desc; Argument() : arg_name{}, arg_letter{}, arg_value{}, arg_type{}, desc{} {} Argument(const Argument &a) : arg_name{a.arg_name}, arg_letter{a.arg_letter}, arg_value{a.arg_value}, arg_type{a.arg_type}, desc{a.desc} {} Argument &operator=(const Argument<String> &a) { arg_name = a.arg_name; arg_letter = a.arg_letter; arg_value = a.arg_value; arg_type = a.arg_type; desc = a.desc; return *this; } auto operator<=>(const Argument<String> &a) const { return (arg_letter <=> a.arg_letter); } }; template <StringType String> struct ArgumentData { std::vector<String> args; int argc; ArgumentData() = default; ArgumentData(const ArgumentData<String> &a) : args{a.args}, argc{a.argc} {} ArgumentData &operator=(const ArgumentData<String> &a) { if (!args.empty()) { args.erase(args.begin(), args.end()); } std::copy(a.args.begin(), a.args.end(), std::back_inserter(args)); argc = a.argc; return *this; } ArgumentData(ArgumentData<String> &&a) : args{std::move(a.args)}, argc{a.argc} {} ArgumentData<String> &operator=(ArgumentData<String> &&a) { args = std::move(a.args); argc = a.argc; return *this; } }; template <StringType String> class ArgException { public: ArgException() = default; ArgException(const String &s) : value{s} {} String text() const { return value; } private: String value; }; template <StringType String> class Argz { public: Argz() = default; Argz(int argc, char **argv) { initArgs(argc, argv); } Argz(const Argz<String> &a) : arg_data{a.arg_data}, arg_info{a.arg_info}, index{a.index}, cindex{a.cindex} {} Argz<String> &operator=(const Argz<String> &a) { arg_data = a.arg_data; if (!arg_info.empty()) { arg_info.erase(arg_info.begin(), arg_info.end()); } for (const auto &i : a.arg_info) { arg_info[i.first] = i.second; } index = a.index; cindex = a.cindex; return *this; } Argz(Argz<String> &&a) : arg_data{std::move(a.arg_data)}, arg_info{std::move(a.arg_info)}, index{a.index}, cindex{a.cindex} {} Argz<String> &operator=(Argz<String> &&a) { arg_data = std::move(a.arg_data); arg_info = std::move(a.arg_info); index = a.index; cindex = a.cindex; return *this; } Argz<String> &initArgs(int argc, char **argv) { arg_data.argc = argc; if constexpr (std::is_same<typename String::value_type, char>::value) { for (int i = 1; i < argc; ++i) { const char *a = argv[i]; arg_data.args.push_back(a); } reset(); return *this; } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { for (int i = 1; i < argc; ++i) { const char *a = argv[i]; String data; for (size_t z = 0; a[z] != 0; ++z) { data += static_cast<typename String::value_type>(a[z]); } arg_data.args.push_back(data); } reset(); return *this; } reset(); return *this; } void reset() { index = 0; cindex = 1; } Argz<String> &addOptionSingle(const int &c, const String &description) { Argument<String> a{}; a.arg_letter = c; a.arg_type = ArgType::ARG_SINGLE; a.desc = description; arg_info[c] = a; return *this; } Argz<String> &addOptionSingleValue(const int &c, const String &description) { Argument<String> a{}; a.arg_letter = c; a.arg_type = ArgType::ARG_SINGLE_VALUE; a.desc = description; arg_info[c] = a; return *this; } Argz<String> &addOptionDouble(const int &code, const String &value, const String &description) { Argument<String> a{}; a.arg_letter = code; a.arg_type = ArgType::ARG_DOUBLE; a.desc = description; a.arg_name = value; arg_info[code] = a; return *this; } Argz<String> &addOptionDoubleValue(const int &code, const String &value, const String &description) { Argument<String> a{}; a.arg_letter = code; a.arg_type = ArgType::ARG_DOUBLE_VALUE; a.desc = description; a.arg_name = value; arg_info[code] = a; return *this; } int lookUpCode(const String &value) { for (const auto &i : arg_info) { if (i.second.arg_name == value) { return i.second.arg_letter; } } return -1; } int proc(Argument<String> &a) { if (index < static_cast<int>(arg_data.args.size())) { const String &type{arg_data.args[index]}; if (type.length() > 3 && type[0] == '-' && type[1] == '-') { String name{}; for (size_t z = 2; z < type.length(); ++z) name += type[z]; int code = lookUpCode(name); if (code != -1) { auto pos = arg_info.find(code); if (pos != arg_info.end()) { if (pos->second.arg_type == ArgType::ARG_DOUBLE) { a = pos->second; a.arg_name = name; index++; return code; } else { a = pos->second; a.arg_name = name; if (++index < static_cast<int>(arg_data.args.size()) && arg_data.args[index][0] != '-') { a.arg_name = name; a.arg_value = arg_data.args[index]; index++; return code; } else { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Expected Value"); } } } } } else { if constexpr (std::is_same<typename String::value_type, char>::value) { String value = "Error argument: "; value += name; value += " switch not found"; throw ArgException<String>(value); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { String value = L"Error argument: "; value += name; value += L" switch not found"; throw ArgException<String>(value); } } } else if (type.length() == 1 && type[0] == '-') { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Expected Value found -"); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { throw ArgException<String>(L"Expected Value found -"); } } else if (type.length() > 1 && (type[0] == '-')) { const int c{type[cindex]}; const auto pos{arg_info.find(c)}; cindex++; if (cindex >= static_cast<int>(type.length())) { cindex = 1; index++; } String name_val{}; name_val += static_cast<typename String::value_type>(c); if (pos != arg_info.end()) { if (pos->second.arg_type == ArgType::ARG_SINGLE) { a = pos->second; a.arg_name = name_val; return c; } else if (pos->second.arg_type == ArgType::ARG_SINGLE_VALUE) { if (index < static_cast<int>(arg_data.args.size())) { const String &s{arg_data.args[index]}; if (s.length() > 1 && s[0] == '-' && !(s[1] >= '0' && s[1] <= '9')) { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Expected value"); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { throw ArgException<String>(L"Expected value"); } } else if (s.length() == 1 && s[0] == '-') { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Expected Value found -"); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { throw ArgException<String>(L"Expected Value found -"); } } if (s.length() > 0) { a = pos->second; a.arg_value = s; a.arg_name = name_val; index++; return c; } } else { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Expected Value"); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { throw ArgException<String>(L"Expected Value"); } } } else { if constexpr (std::is_same<typename String::value_type, char>::value) { throw ArgException<String>("Invalid switch not found!"); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { throw ArgException<String>(L"Invalid switch not found!"); } } } else { if constexpr (std::is_same<typename String::value_type, char>::value) { String value; value = "Error argument "; value += static_cast<typename String::value_type>(c); value += " switch not found."; throw ArgException<String>(value); } if constexpr (std::is_same<typename String::value_type, wchar_t>::value) { String value; value = L"Error argument "; value += static_cast<typename String::value_type>(c); value += L" switch not found."; throw ArgException<String>(value); } } } else { a = Argument<String>(); a.arg_name = String{}; a.arg_type = ArgType::ARG_NONE; a.arg_name = a.arg_value = arg_data.args.at(index); index++; return '-'; } } return -1; } template <typename T> void help(T &cout) { using char_type = typename std::decay<decltype(*std::declval<T>().rdbuf())>::type::char_type; std::vector<Argument<String>> v; std::vector<Argument<String>> v2; for (const auto &i : arg_info) { if (i.second.arg_type == ArgType::ARG_SINGLE || i.second.arg_type == ArgType::ARG_SINGLE_VALUE) v.push_back(i.second); else if (i.second.arg_type == ArgType::ARG_DOUBLE || i.second.arg_type == ArgType::ARG_DOUBLE_VALUE) v2.push_back(i.second); } std::sort(v.begin(), v.end()); std::sort(v2.begin(), v2.end()); std::vector<Argument<String>> farg; farg.reserve(v.size() + v2.size()); std::copy(v.begin(), v.end(), std::back_inserter(farg)); std::copy(v2.begin(), v2.end(), std::back_inserter(farg)); for (auto a = farg.begin(); a != farg.end(); ++a) { if (a->arg_type == ArgType::ARG_SINGLE || a->arg_type == ArgType::ARG_SINGLE_VALUE) { if constexpr (std::is_same<char_type, char>::value) { String item; item += static_cast<char_type>(a->arg_letter); cout << "-" << std::setfill(' ') << std::setw(9) << std::left << item << "\t"; cout << std::setfill(' ') << std::left << std::setw(10) << a->desc; cout << '\n'; } else if constexpr (std::is_same<char_type, wchar_t>::value) { String item; item += static_cast<char_type>(a->arg_letter); cout << L"-" << std::setfill(L' ') << std::setw(9) << std::left << item << L"\t"; cout << std::setfill(L' ') << std::setw(10) << a->desc; cout << L'\n'; } } else { if constexpr (std::is_same<char_type, char>::value) { cout << "--"; cout << std::setfill(' ') << std::left << std::setw(10) << a->arg_name; cout << "\t"; cout << std::setw(10) << a->desc; cout << '\n'; } else if constexpr (std::is_same<char_type, wchar_t>::value) { cout << L"--"; cout << std::setfill(L' ') << std::left << std::setw(10) << a->arg_name; cout << L"\t"; cout << std::setw(10) << std::left << a->desc; cout << L'\n'; } } } } protected: ArgumentData<String> arg_data; std::unordered_map<int, Argument<String>> arg_info; private: int index = 0, cindex = 1; }; #endif