1//! @file argument.hpp
2//! @author ryftchen
3//! @brief The declarations (argument) in the utility module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#pragma once
8
9#include <algorithm>
10#include <any>
11#include <cstdint>
12#include <functional>
13#include <list>
14#include <map>
15#include <optional>
16#include <sstream>
17#include <variant>
18
19//! @brief The utility module.
20namespace utility // NOLINT(modernize-concat-nested-namespaces)
21{
22//! @brief Argument-parsing-related functions in the utility module.
23namespace argument
24{
25//! @brief Brief function description.
26//! @return function description (module_function)
27inline static const char* description() noexcept
28{
29 return "UTIL_ARGUMENT";
30}
31extern const char* version() noexcept;
32
33//! @brief Confirm container traits. Value is false.
34//! @tparam Var - type of variety to be confirmed
35template <typename Var, typename = void>
36struct HasContainerTraits : public std::false_type
37{
38};
39//! @brief Confirm container traits (std::string). Value is false.
40template <>
41struct HasContainerTraits<std::string> : public std::false_type
42{
43};
44//! @brief Confirm container traits (std::string_view). Value is false.
45template <>
46struct HasContainerTraits<std::string_view> : public std::false_type
47{
48};
49//! @brief Confirm container traits. Value is true.
50//! @tparam Var - type of variety to be confirmed
51template <typename Var>
52struct HasContainerTraits<
53 Var,
54 std::void_t<
55 typename Var::value_type,
56 decltype(std::declval<Var>().begin()),
57 decltype(std::declval<Var>().end()),
58 decltype(std::declval<Var>().size())>> : public std::true_type
59{
60};
61//! @brief Confirm whether it is a container.
62//! @tparam Var - type of variety to be confirmed
63template <typename Var>
64static constexpr bool isContainer = HasContainerTraits<Var>::value;
65
66//! @brief Confirm streamable traits. Value is false.
67//! @tparam Var - type of variety to be confirmed
68template <typename Var, typename = void>
69struct HasStreamableTraits : public std::false_type
70{
71};
72//! @brief Confirm streamable traits. Value is true.
73//! @tparam Var - type of variety to be confirmed
74template <typename Var>
75struct HasStreamableTraits<Var, std::void_t<decltype(std::declval<std::ostream&>() << std::declval<Var>())>>
76 : public std::true_type
77{
78};
79//! @brief Confirm whether it is streamable.
80//! @tparam Var - type of variety to be confirmed
81template <typename Var>
82static constexpr bool isStreamable = HasStreamableTraits<Var>::value;
83
84//! @brief Enumerate specific argument patterns.
85enum class ArgsNumPattern : std::uint8_t
86{
87 //! @brief Optional.
88 optional,
89 //! @brief Any.
90 any,
91 //! @brief At least one.
92 atLeastOne
93};
94//! @brief Indicate the range for the number of arguments.
95class ArgsNumRange
96{
97public:
98 //! @brief Construct a new ArgsNumRange object.
99 //! @param minimum - minimum of range
100 //! @param maximum - maximum of range
101 ArgsNumRange(const std::size_t minimum, const std::size_t maximum);
102
103 //! @brief The operator (==) overloading of ArgTrait class.
104 //! @param rhs - right-hand side
105 //! @return be equal or not
106 bool operator==(const ArgsNumRange& rhs) const;
107 //! @brief The operator (!=) overloading of ArgTrait class.
108 //! @param rhs - right-hand side
109 //! @return be unequal or not
110 bool operator!=(const ArgsNumRange& rhs) const;
111
112 friend class ArgTrait;
113 //! @brief Check whether the number of arguments is within the range.
114 //! @param value - number of arguments
115 //! @return within or not
116 [[nodiscard]] bool within(const std::size_t value) const;
117 //! @brief Check whether the number of arguments is exact.
118 //! @return be exact or not
119 [[nodiscard]] bool isExact() const;
120 //! @brief Check whether the maximum of the range is set.
121 //! @return exist or not
122 [[nodiscard]] bool existRightBound() const;
123
124private:
125 //! @brief Minimum of range.
126 std::size_t min{0};
127 //! @brief Maximum of range.
128 std::size_t max{0};
129
130protected:
131 friend std::ostream& operator<<(std::ostream& os, const ArgsNumRange& range);
132};
133
134class Argument;
135
136//! @brief Argument trait.
137class ArgTrait
138{
139public:
140 //! @brief Construct a new ArgTrait object.
141 //! @tparam Num - number of arguments
142 //! @tparam Is - indices of sequence related to arguments
143 //! @param prefix - prefix characters
144 //! @param collection - collection of arguments to be registered
145 //! @param sequence - sequence related to arguments
146 template <std::size_t Num, std::size_t... Is>
147 explicit ArgTrait(
148 const std::string_view prefix,
149 std::array<std::string_view, Num>&& collection,
150 const std::index_sequence<Is...>& sequence);
151 //! @brief Construct a new ArgTrait object.
152 //! @tparam Num - number of arguments
153 //! @param prefix - prefix characters
154 //! @param collection - collection of arguments to be registered
155 template <std::size_t Num>
156 explicit ArgTrait(const std::string_view prefix, std::array<std::string_view, Num>&& collection) :
157 ArgTrait(prefix, std::move(collection), std::make_index_sequence<Num>{})
158 {
159 }
160
161 //! @brief The operator (==) overloading of ArgTrait class.
162 //! @tparam RHS - type of right-hand side
163 //! @param rhs - right-hand side
164 //! @return be equal or not
165 template <typename RHS>
166 bool operator==(const RHS& rhs) const;
167 //! @brief The operator (!=) overloading of ArgTrait class.
168 //! @tparam RHS - type of right-hand side
169 //! @param rhs - right-hand side
170 //! @return be unequal or not
171 template <typename RHS>
172 bool operator!=(const RHS& rhs) const;
173
174 //! @brief Set help message.
175 //! @param message - help message
176 //! @return reference of the ArgTrait object
177 ArgTrait& help(const std::string_view message);
178 //! @brief Set meta variable.
179 //! @param variable - meta variable
180 //! @return reference of the ArgTrait object
181 ArgTrait& metaVariable(const std::string_view variable);
182 //! @brief Set default value.
183 //! @tparam Value - type of default value
184 //! @param value - default value
185 //! @return reference of the ArgTrait object
186 template <typename Value>
187 ArgTrait& defaultValue(Value&& value);
188 //! @brief Set default value.
189 //! @param value - default value
190 //! @return reference of the ArgTrait object
191 ArgTrait& defaultValue(const std::string_view value);
192 //! @brief Set implicit value.
193 //! @param value - implicit value
194 //! @return reference of the ArgTrait object
195 ArgTrait& implicitValue(std::any value);
196 //! @brief Set the argument property to be required.
197 //! @return reference of the ArgTrait object
198 ArgTrait& required();
199 //! @brief Set the argument property to be appending.
200 //! @return reference of the ArgTrait object
201 ArgTrait& appending();
202 //! @brief Set the argument property to be remaining.
203 //! @return reference of the ArgTrait object
204 ArgTrait& remaining();
205 //! @brief The action of specific arguments.
206 //! @tparam Func - type of callable function
207 //! @tparam Args - type of bound arguments
208 //! @param callable - callable function
209 //! @param boundArgs - bound arguments
210 //! @return reference of the ArgTrait object
211 template <typename Func, typename... Args>
212 auto action(Func&& callable, Args&&... boundArgs)
213 -> std::enable_if_t<std::is_invocable_v<Func, Args..., const std::string&>, ArgTrait&>;
214 //! @brief Set number of arguments.
215 //! @param num - number of arguments
216 //! @return reference of the ArgTrait object
217 ArgTrait& argsNum(const std::size_t num);
218 //! @brief Set minimum number and maximum number of arguments.
219 //! @param numMin - minimum number
220 //! @param numMax - maximum number
221 //! @return reference of the ArgTrait object
222 ArgTrait& argsNum(const std::size_t numMin, const std::size_t numMax);
223 //! @brief Set number of arguments with pattern.
224 //! @param pattern - argument pattern
225 //! @return reference of the ArgTrait object
226 ArgTrait& argsNum(const ArgsNumPattern pattern);
227 //! @brief Consume arguments.
228 //! @tparam Iterator - type of argument iterator
229 //! @param start - start argument iterator
230 //! @param end - end argument iterator
231 //! @param argName - target argument name
232 //! @return argument iterator between start and end
233 template <typename Iterator>
234 Iterator consume(const Iterator start, Iterator end, const std::string_view argName = {});
235 //! @brief Validate all arguments.
236 void validate() const;
237 //! @brief Get the inline usage.
238 //! @return inline usage
239 [[nodiscard]] std::string getInlineUsage() const;
240 //! @brief Get the length of all arguments.
241 //! @return length of all arguments
242 [[nodiscard]] std::size_t getArgumentsLength() const;
243 friend class Argument;
244
245private:
246 //! @brief Alias for the function which has valued return.
247 using ValuedAction = std::function<std::any(const std::string&)>;
248 //! @brief Alias for the function which has void return.
249 using VoidAction = std::function<void(const std::string&)>;
250 //! @brief All argument names.
251 std::vector<std::string> names;
252 //! @brief Used argument name.
253 std::string usedName;
254 //! @brief Help message.
255 std::string helpMsg;
256 //! @brief Meta variable.
257 std::string metaVar;
258 //! @brief Default value.
259 std::any defaultVal;
260 //! @brief Default value content to be represented.
261 std::string representedDefVal;
262 //! @brief Implicit value.
263 std::any implicitVal;
264 //! @brief All actions of arguments.
265 std::variant<ValuedAction, VoidAction> actions{
266 std::in_place_type<ValuedAction>, [](const std::string& value) { return value; }};
267 //! @brief Values from all arguments.
268 std::vector<std::any> values;
269 //! @brief Flag to indicate whether to accept optional like value.
270 bool optionalAsValue{false};
271 //! @brief Flag to indicate whether to be optional.
272 bool isOptional{true};
273 //! @brief Flag to indicate whether to be required.
274 bool isRequired{true};
275 //! @brief Flag to indicate whether to be repeatable.
276 bool isRepeatable{true};
277 //! @brief Flag to indicate whether to be used.
278 bool isUsed{true};
279 //! @brief End of file in arguments.
280 static constexpr int eof{std::char_traits<char>::eof()};
281 //! @brief Prefix characters.
282 std::string prefixChars;
283 //! @brief Maximum size for representing.
284 static constexpr std::size_t maxRepresentSize{5};
285 //! @brief The range for the number of arguments.
286 ArgsNumRange argsNumRange{1, 1};
287
288 //! @brief Represent target value.
289 //! @tparam Value - type of target value
290 //! @param value - target value
291 //! @return content to be represented
292 template <typename Value>
293 static std::string represent(const Value& value);
294 //! @brief Implementation of wrapping function calls that have scope.
295 //! @tparam Func - type of callable function
296 //! @tparam Tuple - type of bound arguments tuple
297 //! @tparam Extra - type of extra option
298 //! @tparam Is - number of sequence which converted from bound arguments tuple
299 //! @param func - callable function
300 //! @param tup - bound arguments tuple
301 //! @param ext - extra option
302 //! @param seq - sequence which converted from bound arguments tuple
303 //! @return wrapping of calls
304 template <typename Func, typename Tuple, typename Extra, std::size_t... Is>
305 static constexpr decltype(auto) applyScopedOneImpl(
306 Func&& func, Tuple&& tup, Extra&& ext, const std::index_sequence<Is...>& seq);
307 //! @brief Wrap function calls that have scope.
308 //! @tparam Func - type of callable function
309 //! @tparam Tuple - type of bound arguments tuple
310 //! @tparam Extra - type of extra option
311 //! @param func - callable function
312 //! @param tup - bound arguments tuple
313 //! @param ext - extra option
314 //! @return wrapping of calls
315 template <typename Func, typename Tuple, typename Extra>
316 static constexpr decltype(auto) applyScopedOne(Func&& func, Tuple&& tup, Extra&& ext);
317 //! @brief Throw an exception when the range for the number of arguments is invalid.
318 [[noreturn]] void throwInvalidArgsNumRange() const;
319 //! @brief Find the character in the argument.
320 //! @param name - name of argument
321 //! @return character
322 static int lookAhead(const std::string_view name);
323 //! @brief Check whether the argument is optional.
324 //! @param name - name of argument
325 //! @param prefix - prefix characters
326 //! @return be optional or not
327 static bool checkIfOptional(const std::string_view name, const std::string_view prefix);
328 //! @brief Check whether the argument is positional.
329 //! @param name - name of argument
330 //! @param prefix - prefix characters
331 //! @return be positional or not
332 static bool checkIfPositional(const std::string_view name, const std::string_view prefix);
333 //! @brief Get the value.
334 //! @tparam Value - type of value
335 //! @return value corresponding to the specific type
336 template <typename Value>
337 Value get() const;
338 //! @brief Retrieves the value of the argument, if any.
339 //! @tparam Value - type of value
340 //! @return an optional that contains the value of the argument if it exists, otherwise an empty optional
341 template <typename Value>
342 std::optional<Value> present() const;
343 //! @brief Convert the container type.
344 //! @tparam Container - type of container
345 //! @param operand - container to be converted
346 //! @return container after converting type
347 template <typename Container>
348 static Container anyCastContainer(const std::vector<std::any>& operand);
349 //! @brief Wrap for applying actions.
350 //! @tparam Iterator - type of argument iterator
351 template <typename Iterator>
352 struct ApplyAction
353 {
354 //! @brief The first argument iterator.
355 const Iterator first{};
356 //! @brief The last argument iterator.
357 const Iterator last{};
358 //! @brief The ArgTrait instance.
359 ArgTrait& self;
360
361 //! @brief The operator (()) overloading of ApplyAction struct.
362 //! @param func - function which has valued return
363 void operator()(const ValuedAction& func) const
364 {
365 std::transform(first, last, std::back_inserter(x&: self.values), func);
366 }
367 //! @brief The operator (()) overloading of ApplyAction struct.
368 //! @param func - function which has void return
369 void operator()(const VoidAction& func) const
370 {
371 std::for_each(first, last, func);
372 if (!self.defaultVal.has_value() && !self.optionalAsValue)
373 {
374 self.values.resize(new_size: static_cast<std::size_t>(std::distance(first, last)));
375 }
376 }
377 };
378
379protected:
380 friend std::ostream& operator<<(std::ostream& os, const ArgTrait& tra);
381 friend std::ostream& operator<<(std::ostream& os, const Argument& arg);
382};
383
384template <std::size_t Num, std::size_t... Is>
385ArgTrait::ArgTrait(
386 const std::string_view prefix,
387 std::array<std::string_view, Num>&& collection,
388 const std::index_sequence<Is...>& /*sequence*/) :
389 isOptional{(checkIfOptional(name: collection.at(Is), prefix) || ...)},
390 isRequired{false},
391 isRepeatable{false},
392 isUsed{false},
393 prefixChars{prefix}
394{
395 (names.emplace_back(std::move(collection).at(Is)), ...);
396 std::sort(
397 names.begin(),
398 names.end(),
399 [](const auto& lhs, const auto& rhs)
400 { return (lhs.size() == rhs.size()) ? (lhs < rhs) : (lhs.size() < rhs.size()); });
401}
402
403template <typename RHS>
404bool ArgTrait::operator==(const RHS& rhs) const
405{
406 if constexpr (!isContainer<RHS>)
407 {
408 return rhs == get<RHS>();
409 }
410 else
411 {
412 const auto& lhs = get<RHS>();
413 return std::equal(
414 std::cbegin(lhs),
415 std::cend(lhs),
416 std::cbegin(rhs),
417 std::cend(rhs),
418 [](const auto& lhs, const auto& rhs)
419 { return rhs == std::any_cast<const typename RHS::value_type&>(lhs); });
420 }
421}
422
423template <typename RHS>
424bool ArgTrait::operator!=(const RHS& rhs) const
425{
426 return !(rhs == *this);
427}
428
429template <typename Value>
430ArgTrait& ArgTrait::defaultValue(Value&& value)
431{
432 representedDefVal = represent(value);
433 defaultVal = std::forward<Value>(value);
434 return *this;
435}
436
437template <typename Func, typename... Args>
438auto ArgTrait::action(Func&& callable, Args&&... boundArgs)
439 -> std::enable_if_t<std::is_invocable_v<Func, Args..., const std::string&>, ArgTrait&>
440{
441 using ActionType = std::conditional_t<
442 std::is_void_v<std::invoke_result_t<Func, Args..., const std::string&>>,
443 VoidAction,
444 ValuedAction>;
445 if constexpr (!sizeof...(Args))
446 {
447 actions.emplace<ActionType>(std::forward<Func>(callable));
448 }
449 else
450 {
451 actions.emplace<ActionType>([func = std::forward<Func>(callable),
452 tup = std::make_tuple(std::forward<Args>(boundArgs)...)](const std::string& opt)
453 { return applyScopedOne(func, tup, opt); });
454 }
455 return *this;
456}
457
458template <typename Iterator>
459Iterator ArgTrait::consume(const Iterator start, Iterator end, const std::string_view argName)
460{
461 if (!isRepeatable && isUsed)
462 {
463 throw std::runtime_error{"Duplicate argument."};
464 }
465
466 isUsed = true;
467 usedName = argName;
468 const auto numMin = argsNumRange.min;
469 const auto numMax = argsNumRange.max;
470 if (numMax == 0)
471 {
472 values.emplace_back(args&: implicitVal);
473 std::visit([](const auto& func) { func({}); }, actions);
474 return start;
475 }
476 if (auto dist = static_cast<std::size_t>(std::distance(start, end)); dist >= numMin)
477 {
478 if (dist > numMax)
479 {
480 end = std::next(start, static_cast<typename Iterator::difference_type>(numMax));
481 }
482 if (!optionalAsValue)
483 {
484 end = std::find_if(start, end, std::bind(f&: checkIfOptional, args: std::placeholders::_1, args&: prefixChars));
485 dist = static_cast<std::size_t>(std::distance(start, end));
486 if (dist < numMin)
487 {
488 throw std::runtime_error{"Too few arguments."};
489 }
490 }
491 std::visit(ApplyAction<Iterator>{start, end, *this}, actions);
492 return end;
493 }
494 if (defaultVal.has_value())
495 {
496 return start;
497 }
498 throw std::runtime_error{"Too few arguments for '" + usedName + "'."};
499}
500
501template <typename Value>
502std::string ArgTrait::represent(const Value& value)
503{
504 if constexpr (std::is_same_v<Value, bool>)
505 {
506 return value ? "true" : "false";
507 }
508 else if constexpr (std::is_convertible_v<Value, std::string_view>)
509 {
510 return '"' + value + '"';
511 }
512 else if constexpr (isContainer<Value>)
513 {
514 std::ostringstream out{};
515 out << '{';
516 const auto size = value.size();
517 if (size > 1)
518 {
519 out << represent(*std::cbegin(value));
520 std::for_each(
521 std::next(std::cbegin(value)),
522 std::next(
523 std::cbegin(value),
524 static_cast<typename Value::iterator::difference_type>(
525 std::min<std::size_t>(size, maxRepresentSize) - 1)),
526 [&out](const auto& v) { out << ' ' << represent(v); });
527 if (size <= maxRepresentSize)
528 {
529 out << ' ';
530 }
531 else
532 {
533 out << " ... ";
534 }
535 }
536 if (size > 0)
537 {
538 out << represent(*std::prev(std::cend(value)));
539 }
540 out << '}';
541 return std::move(out).str();
542 }
543 else if constexpr (isStreamable<Value>)
544 {
545 std::ostringstream out{};
546 out << value;
547 return std::move(out).str();
548 }
549 else
550 {
551 return " not representable ";
552 }
553}
554
555template <typename Func, typename Tuple, typename Extra, std::size_t... Is>
556constexpr decltype(auto) ArgTrait::applyScopedOneImpl(
557 Func&& func, Tuple&& tup, Extra&& ext, const std::index_sequence<Is...>& /*seq*/)
558{
559 return std::invoke(std::forward<Func>(func), std::get<Is>(std::forward<Tuple>(tup))..., std::forward<Extra>(ext));
560}
561
562template <typename Func, typename Tuple, typename Extra>
563constexpr decltype(auto) ArgTrait::applyScopedOne(Func&& func, Tuple&& tup, Extra&& ext)
564{
565 return applyScopedOneImpl(
566 std::forward<Func>(func),
567 std::forward<Tuple>(tup),
568 std::forward<Extra>(ext),
569 std::make_index_sequence<std::tuple_size_v<std::remove_reference_t<Tuple>>>{});
570}
571
572template <typename Value>
573Value ArgTrait::get() const
574{
575 if (!values.empty())
576 {
577 if constexpr (isContainer<Value>)
578 {
579 return anyCastContainer<Value>(values);
580 }
581 else
582 {
583 return std::any_cast<Value>(values.front());
584 }
585 }
586 if (defaultVal.has_value())
587 {
588 return std::any_cast<Value>(defaultVal);
589 }
590 if constexpr (isContainer<Value>)
591 {
592 if (!optionalAsValue)
593 {
594 return anyCastContainer<Value>(values);
595 }
596 }
597 throw std::runtime_error{"No value specified for '" + names.back() + "'."};
598}
599
600template <typename Value>
601std::optional<Value> ArgTrait::present() const
602{
603 if (defaultVal.has_value())
604 {
605 throw std::runtime_error{"Default value always presents."};
606 }
607 if (values.empty())
608 {
609 return std::nullopt;
610 }
611 if constexpr (isContainer<Value>)
612 {
613 return anyCastContainer<Value>(values);
614 }
615 return std::any_cast<Value>(values.front());
616}
617
618template <typename Container>
619Container ArgTrait::anyCastContainer(const std::vector<std::any>& operand)
620{
621 Container result{};
622 std::transform(
623 operand.cbegin(),
624 operand.cend(),
625 std::back_inserter(result),
626 [](const auto& value) { return std::any_cast<typename Container::value_type>(value); });
627 return result;
628}
629
630//! @brief Parse arguments.
631class Argument
632{
633public:
634 //! @brief Construct a new Argument object.
635 //! @param title - title name
636 //! @param version - version number
637 explicit Argument(const std::string_view title = {}, const std::string_view version = "1.0.0") :
638 titleName{title}, versionNumber{version}, parserPath{title}
639 {
640 }
641 //! @brief Destroy the Argument object.
642 virtual ~Argument() = default;
643 //! @brief Construct a new Argument object.
644 //! @param arg - object for copy constructor
645 Argument(const Argument& arg);
646 //! @brief Construct a new Argument object.
647 Argument(Argument&&) noexcept = default;
648 //! @brief The operator (=) overloading of Argument class.
649 //! @param arg - object for copy assignment operator
650 //! @return reference of the Argument object
651 Argument& operator=(const Argument& arg);
652 //! @brief The operator (=) overloading of Argument class.
653 //! @return reference of the Argument object
654 Argument& operator=(Argument&&) noexcept = default;
655
656 //! @brief The operator (bool) overloading of Argument class.
657 explicit operator bool() const;
658 //! @brief The operator ([]) overloading of ArgTrait class.
659 //! @param argName - target argument name
660 //! @return reference of the ArgTrait object
661 ArgTrait& operator[](const std::string_view argName) const;
662
663 //! @brief Add a single argument.
664 //! @tparam Args - type of arguments
665 //! @param fewArgs - argument name
666 //! @return reference of the ArgTrait object
667 template <typename... Args>
668 ArgTrait& addArgument(Args... fewArgs);
669 //! @brief Add a descrText.
670 //! @param text - descrText text
671 //! @return reference of the Argument object
672 Argument& addDescription(const std::string_view text);
673 //! @brief Get the ArgTrait or Argument instance by name.
674 //! @tparam Inst - type of instance
675 //! @param name - instance name
676 //! @return ArgTrait or Argument instance
677 template <typename Inst = ArgTrait>
678 Inst& at(const std::string_view name);
679 //! @brief Parse all input arguments.
680 //! @param arguments - container of all arguments
681 void parseArgs(const std::vector<std::string>& arguments);
682 //! @brief Parse all input arguments.
683 //! @param argc - argument count
684 //! @param argv - argument vector
685 void parseArgs(const int argc, const char* const argv[]);
686 //! @brief Get argument value by name.
687 //! @tparam Arg - type of argument
688 //! @param argName - target argument name
689 //! @return argument value
690 template <typename Arg = std::string>
691 Arg get(const std::string_view argName) const;
692 //! @brief Retrieves the value of the argument, if any.
693 //! @tparam Arg - type of argument
694 //! @param argName - target argument name
695 //! @return an optional that contains the value of the argument if it exists, otherwise an empty optional
696 template <typename Arg = std::string>
697 std::optional<Arg> present(const std::string_view argName) const;
698 //! @brief Check whether the argument is used.
699 //! @param argName - target argument name
700 //! @return be used or not
701 bool isUsed(const std::string_view argName) const;
702 //! @brief Check whether the sub-command is used.
703 //! @param subCommandName - target sub-command name
704 //! @return be used or not
705 bool isSubCommandUsed(const std::string_view subCommandName) const;
706 //! @brief Check whether the sub-command is used.
707 //! @param subParser - target sub-parser
708 //! @return be used or not
709 bool isSubCommandUsed(const Argument& subParser) const;
710 //! @brief Clear all used flags.
711 void clearUsed();
712 //! @brief Get the title name.
713 //! @return title name
714 std::string title() const;
715 //! @brief Get the version number.
716 //! @return version number
717 std::string version() const;
718 //! @brief Get the help message content.
719 //! @return help message content
720 std::ostringstream help() const;
721 //! @brief Get the usage content.
722 //! @return usage content
723 std::string usage() const;
724 //! @brief Add a sub-parser.
725 //! @param parser - sub-parser
726 void addSubParser(Argument& parser);
727
728private:
729 //! @brief Title name.
730 std::string titleName;
731 //! @brief Version number.
732 std::string versionNumber;
733
734 //! @brief Alias for the iterator in all ArgTrait instances.
735 using TraitIter = std::list<ArgTrait>::iterator;
736 //! @brief Alias for the iterator in all Argument instances.
737 using ArgumentIter = std::list<std::reference_wrapper<Argument>>::iterator;
738 //! @brief Description text.
739 std::string descrText;
740 //! @brief Prefix characters.
741 std::string prefixChars{"-"};
742 //! @brief Assign characters.
743 std::string assignChars{"="};
744 //! @brief Flag to indicate whether to be parsed.
745 bool isParsed{false};
746 //! @brief List of optional arguments.
747 std::list<ArgTrait> optionalArgs;
748 //! @brief List of positional arguments.
749 std::list<ArgTrait> positionalArgs;
750 //! @brief Mapping table of argument.
751 std::unordered_map<std::string_view, TraitIter> argumentMap;
752 //! @brief Current parser path.
753 std::string parserPath;
754 //! @brief List of sub-parsers.
755 std::list<std::reference_wrapper<Argument>> subParsers;
756 //! @brief Mapping table of sub-parser.
757 std::map<std::string_view, ArgumentIter> subParserMap;
758 //! @brief Mapping table of sub-parser usage.
759 std::map<std::string_view, bool> subParserUsed;
760
761 //! @brief Check whether the prefix character is valid.
762 //! @param c - prefix character
763 //! @return be valid or invalid
764 bool isValidPrefixChar(const char c) const;
765 //! @brief Get any valid prefix character.
766 //! @return valid prefix character
767 char getAnyValidPrefixChar() const;
768 //! @brief Preprocess all raw arguments.
769 //! @param rawArguments - container of all raw arguments
770 //! @return preprocessed argument container
771 std::vector<std::string> preprocessArguments(const std::vector<std::string>& rawArguments) const;
772 //! @brief Parse all input arguments for internal.
773 //! @param rawArguments - container of all raw arguments
774 void parseArgsInternal(const std::vector<std::string>& rawArguments);
775 //! @brief Process the registered argument.
776 //! @tparam Iterator - type of argument iterator
777 //! @param current - current argument iterator
778 //! @param end - end argument iterator
779 //! @param argName - target argument name
780 //! @return argument iterator between current and end
781 template <typename Iterator>
782 Iterator processRegArgument(Iterator current, const Iterator end, const std::string_view argName) const;
783 //! @brief Get the length of the longest argument.
784 //! @return length of the longest argument
785 std::size_t getLengthOfLongestArgument() const;
786 //! @brief Make index for argumentMap.
787 //! @param iterator - iterator in all argument traits
788 void indexArgument(const TraitIter& iterator);
789
790protected:
791 friend std::ostream& operator<<(std::ostream& os, const Argument& arg);
792};
793
794template <typename... Args>
795ArgTrait& Argument::addArgument(Args... fewArgs)
796{
797 const auto argument = optionalArgs.emplace(
798 optionalArgs.cend(), prefixChars, std::array<std::string_view, sizeof...(Args)>{fewArgs...});
799 if (!argument->isOptional)
800 {
801 positionalArgs.splice(positionalArgs.cend(), optionalArgs, argument);
802 }
803 indexArgument(iterator: argument);
804 return *argument;
805}
806
807template <typename Inst>
808Inst& Argument::at(const std::string_view name)
809{
810 if constexpr (std::is_same_v<Inst, ArgTrait>)
811 {
812 return (*this)[name];
813 }
814 else if (const auto subParserIter = subParserMap.find(x: name); subParserIter != subParserMap.cend())
815 {
816 return subParserIter->second->get();
817 }
818 throw std::runtime_error{"No such sub-parser: " + std::string{name} + '.'};
819}
820
821template <typename Arg>
822Arg Argument::get(const std::string_view argName) const
823{
824 if (!isParsed)
825 {
826 throw std::runtime_error{"Nothing parsed, no arguments are available."};
827 }
828 return (*this)[argName].get<Arg>();
829}
830
831template <typename Arg>
832std::optional<Arg> Argument::present(const std::string_view argName) const
833{
834 return (*this)[argName].present<Arg>();
835}
836
837template <typename Iterator>
838Iterator Argument::processRegArgument(Iterator current, const Iterator end, const std::string_view argName) const
839{
840 if (const auto argMapIter = argumentMap.find(x: argName); argMapIter != argumentMap.cend())
841 {
842 const auto argument = argMapIter->second;
843 current = argument->consume(std::next(current), end, argMapIter->first);
844 }
845 else if (const auto& compoundArg = argName; (compoundArg.length() > 1) && isValidPrefixChar(c: compoundArg.at(pos: 0))
846 && !isValidPrefixChar(c: compoundArg.at(pos: 1)))
847 {
848 ++current;
849 for (std::size_t i = 1; i < compoundArg.length(); ++i)
850 {
851 const auto hypotheticalArg = std::string{'-', compoundArg.at(pos: i)};
852 if (const auto argMapIter = argumentMap.find(x: hypotheticalArg); argMapIter != argumentMap.cend())
853 {
854 auto argument = argMapIter->second;
855 current = argument->consume(current, end, argMapIter->first);
856 continue;
857 }
858 throw std::runtime_error{"Unknown argument: " + std::string{argName} + '.'};
859 }
860 }
861 else
862 {
863 throw std::runtime_error{"Unknown argument: " + std::string{argName} + '.'};
864 }
865 return current;
866}
867} // namespace argument
868} // namespace utility
869