fix: windows parsing

This commit is contained in:
2026-03-16 17:41:45 +04:00
parent 8befc60016
commit 182974d61a
6 changed files with 95 additions and 31 deletions

View File

@@ -8,16 +8,28 @@
namespace argument_parser { namespace argument_parser {
using parser = linux_parser; using parser = linux_parser;
} }
namespace argument_parser::v2 {
using parser = linux_parser;
}
#elif __APPLE__ #elif __APPLE__
#include <macos_parser.hpp> #include <macos_parser.hpp>
namespace argument_parser { namespace argument_parser {
using parser = macos_parser; using parser = macos_parser;
} }
namespace argument_parser::v2 {
using parser = macos_parser;
}
#elif _WIN32 #elif _WIN32
#include <windows_parser.hpp> #include <windows_parser.hpp>
namespace argument_parser { namespace argument_parser {
using parser = windows_parser; using parser = windows_parser;
} }
namespace argument_parser::v2 {
using parser = windows_parser;
}
#else #else
#error "Unsupported platform" #error "Unsupported platform"
#endif #endif

View File

@@ -4,7 +4,6 @@
#include <type_traits> #include <type_traits>
#ifndef ARGUMENT_PARSER_HPP #ifndef ARGUMENT_PARSER_HPP
#define ARGUMENT_PARSER_HPP #define ARGUMENT_PARSER_HPP
#include <any> #include <any>
#include <atomic> #include <atomic>
#include <base_convention.hpp> #include <base_convention.hpp>
@@ -158,8 +157,9 @@ namespace argument_parser {
auto id = find_argument_id(arg); auto id = find_argument_id(arg);
if (id.has_value()) { if (id.has_value()) {
auto value = stored_arguments.find(id.value()); auto value = stored_arguments.find(id.value());
if (value != stored_arguments.end() && value->second.has_value()) if (value != stored_arguments.end() && value->second.has_value()) {
return std::any_cast<T>(value->second); return std::any_cast<T>(value->second);
}
} }
return std::nullopt; return std::nullopt;
} }

View File

@@ -38,7 +38,7 @@ namespace argument_parser::v2 {
template <typename T> template <typename T>
void add_argument(std::unordered_map<add_argument_flags, typed_flag_value<T>> const &argument_pairs) { void add_argument(std::unordered_map<add_argument_flags, typed_flag_value<T>> const &argument_pairs) {
add_argument_impl<true, parametered_action<T>>(argument_pairs); add_argument_impl<true, parametered_action<T>, T>(argument_pairs);
} }
template <typename T> void add_argument(std::initializer_list<typed_argument_pair<T>> const &pairs) { template <typename T> void add_argument(std::initializer_list<typed_argument_pair<T>> const &pairs) {
@@ -62,7 +62,7 @@ namespace argument_parser::v2 {
} }
void add_argument(std::unordered_map<add_argument_flags, non_typed_flag_value> const &argument_pairs) { void add_argument(std::unordered_map<add_argument_flags, non_typed_flag_value> const &argument_pairs) {
add_argument_impl<false, non_parametered_action>(argument_pairs); add_argument_impl<false, non_parametered_action, void>(argument_pairs);
} }
argument_parser::base_parser &to_v1() { argument_parser::base_parser &to_v1() {
@@ -91,7 +91,7 @@ namespace argument_parser::v2 {
} }
private: private:
template <bool IsTyped, typename ActionType, typename ArgsMap> template <bool IsTyped, typename ActionType, typename T, typename ArgsMap>
void add_argument_impl(ArgsMap const &argument_pairs) { void add_argument_impl(ArgsMap const &argument_pairs) {
std::unordered_map<extended_add_argument_flags, bool> found_params{ std::unordered_map<extended_add_argument_flags, bool> found_params{
{extended_add_argument_flags::IsTyped, IsTyped}}; {extended_add_argument_flags::IsTyped, IsTyped}};
@@ -142,7 +142,7 @@ namespace argument_parser::v2 {
required); required);
break; break;
case candidate_type::store_other: case candidate_type::store_other:
base::add_argument(short_arg, long_arg, help_text, required); base::add_argument<T>(short_arg, long_arg, help_text, required);
break; break;
default: default:
throw std::runtime_error("Could not match the arguments against any overload."); throw std::runtime_error("Could not match the arguments against any overload.");

View File

@@ -160,9 +160,16 @@ int v1Examples() {
return 0; return 0;
} }
void run_store_point(argument_parser::base_parser const &parser) {
auto point = parser.get_optional<Point>("store-point");
if (point) {
std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl;
}
}
int v2Examples() { int v2Examples() {
using namespace argument_parser::v2::flags; using namespace argument_parser::v2::flags;
argument_parser::v2::base_parser parser; argument_parser::v2::parser parser;
parser.add_argument<std::string>( parser.add_argument<std::string>(
{{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}}); {{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}});
@@ -196,11 +203,17 @@ int v2Examples() {
}); });
parser.on_complete(::run_grep); parser.on_complete(::run_grep);
parser.on_complete(::run_store_point);
parser.handle_arguments(conventions); parser.handle_arguments(conventions);
return 0; return 0;
} }
int main() { int main() {
return v2Examples(); try {
return v2Examples();
} catch (std::exception const &e) {
std::cerr << "Error: " << e.what() << std::endl;
return -1;
}
} }

View File

@@ -115,7 +115,6 @@ namespace argument_parser {
try { try {
argument &corresponding_argument = get_argument(extracted); argument &corresponding_argument = get_argument(extracted);
if (corresponding_argument.expects_parameter()) { if (corresponding_argument.expects_parameter()) {
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) { if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
throw std::runtime_error("expected value for argument " + extracted.second); throw std::runtime_error("expected value for argument " + extracted.second);

View File

@@ -3,26 +3,13 @@
#include "windows_parser.hpp" #include "windows_parser.hpp"
#include <Windows.h> #include <Windows.h>
#include <iostream>
#include <memory> #include <memory>
#include <shellapi.h> #include <shellapi.h>
#include <stdexcept>
#include <string> #include <string>
std::string utf8_from_wstring(const std::wstring &w) { using namespace std::string_literals;
if (w.empty())
return {};
int needed = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (needed <= 0) {
throw std::runtime_error("WideCharToMultiByte sizing failed (" + std::to_string(::GetLastError()) + ")");
}
std::string out;
out.resize(static_cast<size_t>(needed - 1));
int written =
::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, out.data(), needed - 1, nullptr, nullptr);
if (written <= 0) {
throw std::runtime_error("WideCharToMultiByte convert failed (" + std::to_string(::GetLastError()) + ")");
}
return out;
}
struct local_free_deleter { struct local_free_deleter {
void operator()(void *ptr) const { void operator()(void *ptr) const {
@@ -33,28 +20,81 @@ struct local_free_deleter {
} }
}; };
void parse_windows_arguments(std::vector<std::string> &parsed_arguments) { std::string windows_error_message(DWORD error_code) {
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&messageBuffer), 0, nullptr);
if (size == 0 || !messageBuffer) {
return "Unknown Error ("s + std::to_string(error_code) + ")";
}
std::unique_ptr<char, local_free_deleter> smartBuffer(messageBuffer);
std::string result(smartBuffer.get(), size);
result.erase(result.find_last_not_of(" \n\r\t") + 1);
return result;
}
std::string utf8_from_wstring(const std::wstring &w) {
if (w.empty())
return {};
int needed = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, nullptr, 0, nullptr, nullptr);
if (needed <= 0) {
throw std::runtime_error("WideCharToMultiByte sizing failed ("s + windows_error_message(::GetLastError()) +
")");
}
std::string out;
out.resize(needed - 1);
int written =
::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, out.data(), needed, nullptr, nullptr);
if (written <= 0) {
throw std::runtime_error(
"WideCharToMultiByte convert failed, Error("s + windows_error_message(::GetLastError()) + ")" +
" Size (Needed): " + std::to_string(needed) + " Size (Written): " + std::to_string(written) +
" Size (Allocated): " + std::to_string(out.size()));
}
return out;
}
void parse_windows_arguments(std::vector<std::string> &parsed_arguments,
std::function<void(std::string)> const &setProgramName) {
int argc_w; int argc_w;
std::unique_ptr<LPWSTR[], local_free_deleter> argv_w(CommandLineToArgvW(GetCommandLineW(), &argc_w)); std::unique_ptr<LPWSTR[], local_free_deleter> argv_w(CommandLineToArgvW(GetCommandLineW(), &argc_w));
if (argv_w == nullptr) { if (argv_w == nullptr) {
throw std::runtime_error("CommandLineToArgvW failed"); throw std::runtime_error("CommandLineToArgvW failed");
} }
for (int i = 0; i < argc_w; i++) { if (argc_w <= 0) {
std::string arg = utf8_from_wstring(argv_w[i]); return;
parsed_arguments.emplace_back(arg); }
setProgramName(utf8_from_wstring(argv_w[0]));
for (int i = 1; i < argc_w; i++) {
try {
std::string arg = utf8_from_wstring(argv_w[i]);
parsed_arguments.emplace_back(arg);
} catch (std::runtime_error e) {
std::cerr << "Error: " << e.what() << std::endl;
}
} }
} }
namespace argument_parser { namespace argument_parser {
windows_parser::windows_parser() { windows_parser::windows_parser() {
parse_windows_arguments(parsed_arguments); parse_windows_arguments(parsed_arguments,
[this](std::string const &program_name) { this->program_name = program_name; });
} }
} // namespace argument_parser } // namespace argument_parser
namespace argument_parser::v2 { namespace argument_parser::v2 {
windows_parser::windows_parser() { windows_parser::windows_parser() {
parse_windows_arguments(ref_parsed_args()); parse_windows_arguments(ref_parsed_args(),
[this](std::string const &program_name) { this->set_program_name(program_name); });
} }
} // namespace argument_parser::v2 } // namespace argument_parser::v2