-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathArgParser.h
More file actions
142 lines (120 loc) · 4.57 KB
/
Copy pathArgParser.h
File metadata and controls
142 lines (120 loc) · 4.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#pragma once
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
namespace cc {
// ===================================================================
// ArgParser - 一个轻量级的 C++ 命令行参数解析器
// ===================================================================
// 支持的特性:
// - 短参数: -o, -v
// - 长参数: --output, --verbose
// - 值参数: --count=10 或 --count 10
// - 布尔开关: -v, --verbose (出现即为 true)
// - 位置参数: 按顺序匹配的裸值
// - 必填检查: required 参数未传入时报错
// - 帮助信息: --help 自动生成格式化帮助
// ===================================================================
struct Arg {
std::string short_name; // "-o"
std::string long_name; // "--output"
std::string value; // 存储的值(字符串形式)
std::string help; // 帮助文字
bool required{false}; // 是否必填
bool is_flag{false}; // 是否是布尔开关
bool is_set{false}; // 是否已被设置
};
struct PosArg {
std::string name;
std::string help;
bool required;
};
class ArgParser {
public:
ArgParser() = default;
// =================================================================
// 核心接口
// =================================================================
/// 解析命令行参数
void parse(int argc, char *argv[]);
/// 注册命名参数(带值,如 -n 10 或 --count=10)
void add_argument(const std::string &short_name, const std::string &long_name,
const std::string &help, bool required = false);
/// 注册布尔开关(出现即为 true,不接值)
void add_flag(const std::string &short_name, const std::string &long_name,
const std::string &help);
/// 注册位置参数(按注册顺序分配裸值)
void add_positional(const std::string &name, const std::string &help,
bool required = false);
// =================================================================
// 读取接口
// =================================================================
/// 按类型获取参数值(支持 int, float, double 等支持 >> 的类型)
template <typename T> [[nodiscard]] T get(const std::string &key) const {
auto it = args_.find(key);
if (it != args_.end() && !it->second.value.empty()) {
T result{};
std::istringstream iss(it->second.value);
iss >> result;
if (iss.fail()) {
throw std::invalid_argument(
"Argument '" + key + "' = '" + it->second.value +
"' is not convertible to the requested type");
}
return result;
}
auto pit = pos_values.find(key);
if (pit != pos_values.end()) {
T result{};
std::istringstream iss(pit->second);
iss >> result;
if (iss.fail()) {
throw std::invalid_argument(
"Positional '" + key + "' = '" + pit->second +
"' is not convertible to the requested type");
}
return result;
}
throw std::runtime_error("Argument not found: " + key);
}
/// 获取布尔开关的值
[[nodiscard]] bool get_flag(const std::string &key) const;
// 打印帮助信息到 stdout
void print_help(std::string_view program_name) const;
private:
Arg *findLongByShort(const std::string &name) {
for (auto &[k, v] : args_) {
if (v.short_name == name) {
return &v;
}
}
return nullptr;
}
private:
std::unordered_map<std::string, Arg> args_; // 命名参数存储,key 为长名去掉 --
std::vector<PosArg> pos_args; // 位置参数定义(按注册顺序)
std::unordered_map<std::string, std::string> pos_values; // 位置参数值,key 为参数名
};
// =================================================================
// std::string 模板特化
// =================================================================
// 原因:默认的 istringstream 实现无法正确处理带空格的字符串
// 例如:istringstream("hello world") >> str 只会读到 "hello"
// =================================================================
template <>
[[nodiscard]] inline std::string ArgParser::get<std::string>(const std::string &key) const {
auto it = args_.find(key);
if (it != args_.end() && !it->second.value.empty()) {
return it->second.value;
}
auto pit = pos_values.find(key);
if (pit != pos_values.end()) {
return std::string{pit->second};
}
throw std::runtime_error("Argument not found: " + key);
}
} // namespace cc