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 {
using parser = linux_parser;
}
namespace argument_parser::v2 {
using parser = linux_parser;
}
#elif __APPLE__
#include <macos_parser.hpp>
namespace argument_parser {
using parser = macos_parser;
}
namespace argument_parser::v2 {
using parser = macos_parser;
}
#elif _WIN32
#include <windows_parser.hpp>
namespace argument_parser {
using parser = windows_parser;
}
namespace argument_parser::v2 {
using parser = windows_parser;
}
#else
#error "Unsupported platform"
#endif

View File

@@ -4,7 +4,6 @@
#include <type_traits>
#ifndef ARGUMENT_PARSER_HPP
#define ARGUMENT_PARSER_HPP
#include <any>
#include <atomic>
#include <base_convention.hpp>
@@ -158,9 +157,10 @@ namespace argument_parser {
auto id = find_argument_id(arg);
if (id.has_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::nullopt;
}

View File

@@ -38,7 +38,7 @@ namespace argument_parser::v2 {
template <typename T>
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) {
@@ -62,7 +62,7 @@ namespace argument_parser::v2 {
}
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() {
@@ -91,7 +91,7 @@ namespace argument_parser::v2 {
}
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) {
std::unordered_map<extended_add_argument_flags, bool> found_params{
{extended_add_argument_flags::IsTyped, IsTyped}};
@@ -142,7 +142,7 @@ namespace argument_parser::v2 {
required);
break;
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;
default:
throw std::runtime_error("Could not match the arguments against any overload.");

View File

@@ -160,9 +160,16 @@ int v1Examples() {
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() {
using namespace argument_parser::v2::flags;
argument_parser::v2::base_parser parser;
argument_parser::v2::parser parser;
parser.add_argument<std::string>(
{{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_store_point);
parser.handle_arguments(conventions);
return 0;
}
int main() {
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 {
argument &corresponding_argument = get_argument(extracted);
if (corresponding_argument.expects_parameter()) {
if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) {
throw std::runtime_error("expected value for argument " + extracted.second);

View File

@@ -3,26 +3,13 @@
#include "windows_parser.hpp"
#include <Windows.h>
#include <iostream>
#include <memory>
#include <shellapi.h>
#include <stdexcept>
#include <string>
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 (" + 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;
}
using namespace std::string_literals;
struct local_free_deleter {
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;
std::unique_ptr<LPWSTR[], local_free_deleter> argv_w(CommandLineToArgvW(GetCommandLineW(), &argc_w));
if (argv_w == nullptr) {
throw std::runtime_error("CommandLineToArgvW failed");
}
for (int i = 0; i < argc_w; i++) {
if (argc_w <= 0) {
return;
}
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 {
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::v2 {
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