timemory 3.3.0
Modular C++ Toolkit for Performance Analysis and Logging. Profiling API and Tools for C, C++, CUDA, Fortran, and Python. The C++ template API is essentially a framework to creating tools: it is designed to provide a unifying interface for recording various performance measurements alongside data logging and interfaces to other tools.
argparse.hpp
Go to the documentation of this file.
1// MIT License
2//
3// Copyright (c) 2020, The Regents of the University of California,
4// through Lawrence Berkeley National Laboratory (subject to receipt of any
5// required approvals from the U.S. Dept. of Energy). All rights reserved.
6//
7// Permission is hereby granted, free of charge, to any person obtaining a copy
8// of this software and associated documentation files (the "Software"), to deal
9// in the Software without restriction, including without limitation the rights
10// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11// copies of the Software, and to permit persons to whom the Software is
12// furnished to do so, subject to the following conditions:
13//
14// The above copyright notice and this permission notice shall be included in all
15// copies or substantial portions of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23// SOFTWARE.
24
25#pragma once
26
30
31#include <algorithm>
32#include <cassert>
33#include <cstring>
34#include <deque>
35#include <functional>
36#include <iomanip>
37#include <iosfwd>
38#include <list>
39#include <map>
40#include <numeric>
41#include <regex>
42#include <set>
43#include <sstream>
44#include <string>
45#include <type_traits>
46#include <utility>
47#include <vector>
48
49namespace tim
50{
51namespace argparse
52{
53namespace helpers
54{
55//
56//--------------------------------------------------------------------------------------//
57//
58static inline bool
59not_is_space(int ch)
60{
61 return std::isspace(ch) == 0;
62}
63//
64//--------------------------------------------------------------------------------------//
65//
66static inline uint64_t
67lcount(const std::string& s, bool (*f)(int) = not_is_space)
68{
69 uint64_t c = 0;
70 for(size_t i = 0; i < s.length(); ++i, ++c)
71 {
72 if(f(s.at(i)))
73 break;
74 }
75 return c;
76}
77//
78//--------------------------------------------------------------------------------------//
79//
80static inline std::string
81ltrim(std::string s, bool (*f)(int) = not_is_space)
82{
83 s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
84 return s;
85}
86//
87//--------------------------------------------------------------------------------------//
88//
89static inline std::string
90rtrim(std::string s, bool (*f)(int) = not_is_space)
91{
92 s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
93 return s;
94}
95//
96//--------------------------------------------------------------------------------------//
97//
98static inline std::string
99trim(std::string s, bool (*f)(int) = not_is_space)
100{
101 ltrim(s, f);
102 rtrim(s, f);
103 return s;
104}
105//
106//--------------------------------------------------------------------------------------//
107//
108static inline char*
109strdup(const char* s)
110{
111 auto slen = strlen(s);
112 auto* result = new char[slen + 1];
113 if(result)
114 {
115 memcpy(result, s, slen * sizeof(char));
116 result[slen] = '\0';
117 return result;
118 }
119 return nullptr;
120}
121//
122//--------------------------------------------------------------------------------------//
123//
124template <typename InputIt>
125static inline std::string
126join(InputIt begin, InputIt end, const std::string& separator = " ")
127{
128 std::ostringstream ss;
129 if(begin != end)
130 {
131 ss << *begin++;
132 }
133 while(begin != end)
134 {
135 ss << separator;
136 ss << *begin++;
137 }
138 return ss.str();
139}
140//
141//--------------------------------------------------------------------------------------//
142//
143static inline bool
144is_numeric(const std::string& arg)
145{
146 auto _nidx = arg.find_first_of("0123456789");
147 auto _oidx = arg.find_first_not_of("0123456789.Ee+-*/");
148
149 // must have number somewhere
150 if(_nidx == std::string::npos)
151 return false;
152
153 // if something other than number or scientific notation
154 if(_oidx != std::string::npos)
155 return false;
156
157 // numbers + possible scientific notation
158 return true;
159}
160//
161//--------------------------------------------------------------------------------------//
162//
163static inline int
164find_equiv(const std::string& s)
165{
166 for(size_t i = 0; i < s.length(); ++i)
167 {
168 // if find graph symbol before equal, end search
169 // i.e. don't accept --asd)f=0 arguments
170 // but allow --asd_f and --asd-f arguments
171 if(std::ispunct(static_cast<int>(s[i])) != 0)
172 {
173 if(s[i] == '=')
174 {
175 return static_cast<int>(i);
176 }
177 if(s[i] == '_' || s[i] == '-')
178 {
179 continue;
180 }
181 return -1;
182 }
183 }
184 return -1;
185}
186//
187//--------------------------------------------------------------------------------------//
188//
189static inline size_t
190find_punct(const std::string& s)
191{
192 size_t i;
193 for(i = 0; i < s.length(); ++i)
194 {
195 if((std::ispunct(static_cast<int>(s[i])) != 0) && s[i] != '-')
196 {
197 break;
198 }
199 }
200 return i;
201}
202//
203//--------------------------------------------------------------------------------------//
204//
205namespace is_container_impl
206{
207//
208template <typename T>
209struct is_container : std::false_type
210{};
211//
212template <typename... Args>
213struct is_container<std::vector<Args...>> : std::true_type
214{};
215template <typename... Args>
216struct is_container<std::set<Args...>> : std::true_type
217{};
218template <typename... Args>
219struct is_container<std::deque<Args...>> : std::true_type
220{};
221template <typename... Args>
222struct is_container<std::list<Args...>> : std::true_type
223{};
224//
225template <typename T>
226struct is_initializing_container : is_container<T>::type
227{};
228//
229template <typename... Args>
230struct is_initializing_container<std::initializer_list<Args...>> : std::true_type
231{};
232} // namespace is_container_impl
233//
234//--------------------------------------------------------------------------------------//
235//
236// type trait to utilize the implementation type traits as well as decay the type
237template <typename T>
238struct is_container
239{
240 static constexpr bool const value =
241 is_container_impl::is_container<decay_t<T>>::value;
242};
243//
244//--------------------------------------------------------------------------------------//
245//
246// type trait to utilize the implementation type traits as well as decay the type
247template <typename T>
248struct is_initializing_container
249{
250 static constexpr bool const value =
251 is_container_impl::is_initializing_container<decay_t<T>>::value;
252};
253//
254//--------------------------------------------------------------------------------------//
255//
256} // namespace helpers
257
258//
259//--------------------------------------------------------------------------------------//
260//
261// argument vector
262//
263//--------------------------------------------------------------------------------------//
264//
265/// \struct tim::argparse::argument_vector
266/// \brief This class exists to simplify creating argument arrays compatible with execv*
267/// routines and MPI_Comm_spawn/MPI_Comm_spawn_multiple
268///
269struct argument_vector : std::vector<std::string>
270{
271 struct c_args : std::tuple<int, char**, std::string>
272 {
273 using base_type = std::tuple<int, char**, std::string>;
274 template <typename... Args>
275 c_args(Args&&... args)
276 : base_type(std::forward<Args>(args)...)
277 {}
278
279 auto& argc() { return std::get<0>(*this); }
280 auto& argv() { return std::get<1>(*this); }
281 auto& args() { return std::get<2>(*this); }
282
283 const auto& argc() const { return std::get<0>(*this); }
284 const auto& argv() const { return std::get<1>(*this); }
285 const auto& args() const { return std::get<2>(*this); }
286
287 void clear()
288 {
289 // uses comma operator to execute delete and return nullptr
290 for(int i = 0; i < argc(); ++i)
291 argv()[i] = (delete[] argv()[i], nullptr);
292 argv() = (delete[] argv(), nullptr);
293 }
294 };
295
296 using base_type = std::vector<std::string>;
298
299 template <typename... Args>
300 argument_vector(Args&&... args)
301 : base_type(std::forward<Args>(args)...)
302 {}
303
304 explicit argument_vector(int& argc, char**& argv);
305 explicit argument_vector(int& argc, const char**& argv);
306 explicit argument_vector(int& argc, const char* const*& argv);
307 cargs_t get_execv(const base_type& _prepend, size_t _beg = 0,
308 size_t _end = std::numeric_limits<size_t>::max()) const;
309 cargs_t get_execv(size_t _beg = 0,
310 size_t _end = std::numeric_limits<size_t>::max()) const;
311
312 // helper function to free the memory created by get_execv, pass by reference
313 // so that we can set values to nullptr and avoid multiple delete errors
314 static void free_execv(cargs_t& itr) { itr.clear(); }
315};
316//
317//--------------------------------------------------------------------------------------//
318//
319// argument parser
320//
321//--------------------------------------------------------------------------------------//
322//
324{
325 struct arg_result;
326
329 using bool_func_t = std::function<bool(this_type&)>;
330 using action_func_t = std::function<void(this_type&)>;
331 using action_pair_t = std::pair<bool_func_t, action_func_t>;
332 using error_func_t = std::function<void(this_type&, arg_result&)>;
333 using known_args_t = std::tuple<arg_result, int, char**>;
334 using strvec_t = std::vector<std::string>;
335 using strset_t = std::set<std::string>;
336 //
337 //----------------------------------------------------------------------------------//
338 //
340 {
341 arg_result() = default;
343 : m_error(true)
344 , m_what(std::move(err))
345 {}
346
347 operator bool() const { return m_error; }
348
349 friend std::ostream& operator<<(std::ostream& os, const arg_result& dt);
350
351 const std::string& what() const { return m_what; }
352
353 private:
354 bool m_error = false;
355 std::string m_what = {};
356 };
357 //
358 //----------------------------------------------------------------------------------//
359 //
360 struct argument
361 {
362 using callback_t = std::function<void(void*&)>;
363
364 enum Position : int
365 {
367 IgnoreArgument = -2
368 };
369
370 enum Count : int
371 {
372 ANY = -1
373 };
374
375 ~argument() { m_destroy(m_default); }
376
378 {
379 m_names.push_back(name);
380 return *this;
381 }
382
383 argument& names(const std::vector<std::string>& names)
384 {
385 for(const auto& itr : names)
386 m_names.push_back(itr);
387 return *this;
388 }
389
391 {
392 m_desc = description;
393 return *this;
394 }
395
396 argument& dtype(const std::string& _dtype)
397 {
398 m_dtype = _dtype;
399 return *this;
400 }
401
403 {
404 m_required = req;
405 return *this;
406 }
407
409 {
410 if(position != Position::LastArgument)
411 {
412 // position + 1 because technically argument zero is the name of the
413 // executable
414 m_position = position + 1;
415 }
416 else
417 {
418 m_position = position;
419 }
420 return *this;
421 }
422
424 {
425 m_max_count = count;
426 return *this;
427 }
428
430 {
431 m_min_count = count;
432 return *this;
433 }
434
436 {
437 m_count = count;
438 return *this;
439 }
440
441 template <typename T>
442 argument& set_default(const T& val)
443 {
444 m_found = true;
445 m_default_tidx = std::type_index{ typeid(decay_t<T>) };
446 m_callback = [&](void*& obj) {
447 m_destroy(obj);
448 if(!obj)
449 obj = (void*) new T{};
450 (*static_cast<T*>(obj)) = val;
451 };
452 m_destroy = [](void*& obj) {
453 if(obj)
454 delete static_cast<T*>(obj);
455 };
456 return *this;
457 }
458
459 template <typename T>
461 {
462 m_found = true;
463 m_default_tidx = std::type_index{ typeid(decay_t<T>) };
464 m_callback = [&](void*& obj) { obj = (void*) &val; };
465 return *this;
466 }
467
468 template <typename T>
469 argument& choices(const std::initializer_list<T>& _choices)
470 {
471 for(auto&& itr : _choices)
472 {
473 std::stringstream ss;
474 ss << itr;
475 m_choices.insert(ss.str());
476 }
477 return *this;
478 }
479
480 template <template <typename...> class ContainerT, typename T, typename... ExtraT,
481 typename ContT = ContainerT<T, ExtraT...>,
483 argument& choices(const ContainerT<T, ExtraT...>& _choices)
484 {
485 for(auto&& itr : _choices)
486 {
487 std::stringstream ss;
488 ss << itr;
489 m_choices.insert(ss.str());
490 }
491 return *this;
492 }
493
494 template <typename ActionFuncT>
495 argument& action(ActionFuncT&& _func)
496 {
497 m_actions.push_back(std::forward<ActionFuncT>(_func));
498 return *this;
499 }
500
501 bool found() const { return m_found; }
502
503 template <typename T>
504 std::enable_if_t<helpers::is_container<T>::value, T> get()
505 {
506 T t = T{};
507 typename T::value_type vt;
508 for(auto& s : m_values)
509 {
510 std::istringstream in(s);
511 in >> vt;
512 t.insert(t.end(), vt);
513 }
514 if(m_values.empty() && m_default &&
515 m_default_tidx == std::type_index{ typeid(T) })
516 t = (*static_cast<T*>(m_default));
517 return t;
518 }
519
520 template <typename T>
522 !helpers::is_container<T>::value && !std::is_same<T, bool>::value, T>
524 {
525 auto inp = get<std::string>();
526 std::istringstream iss{ inp };
527 T t = T{};
528 iss >> t >> std::ws;
529 if(inp.empty() && m_default && m_default_tidx == std::type_index{ typeid(T) })
530 t = (*static_cast<T*>(m_default));
531 return t;
532 }
533
534 template <typename T>
535 std::enable_if_t<std::is_same<T, bool>::value, T> get()
536 {
537 if(m_count == 0)
538 return found();
539
540 auto inp = get<std::string>();
541 if(inp.empty() && m_default && m_default_tidx == std::type_index{ typeid(T) })
542 return (*static_cast<T*>(m_default));
543 else if(inp.empty())
544 return found();
545
546 return get_bool(inp, found());
547 }
548
549 size_t size() const { return m_values.size(); }
550
552 {
553 std::stringstream ss;
554 for(const auto& itr : m_names)
555 ss << "/" << itr;
556 return ss.str().substr(1);
557 }
558
559 private:
560 argument(const std::string& name, std::string desc, bool required = false)
561 : m_desc(std::move(desc))
562 , m_required(required)
563 {
564 m_names.push_back(name);
565 }
566
567 argument() = default;
568
569 arg_result check_choice(const std::string& value)
570 {
571 if(!m_choices.empty())
572 {
573 if(m_choices.find(value) == m_choices.end())
574 {
575 std::stringstream ss;
576 ss << "Invalid choice: '" << value << "'. Valid choices: ";
577 for(const auto& itr : m_choices)
578 ss << "'" << itr << "' ";
579 return arg_result(ss.str());
580 }
581 }
582 return arg_result{};
583 }
584
585 void execute_actions(argument_parser& p)
586 {
587 for(auto& itr : m_actions)
588 itr(p);
589 }
590
591 friend std::ostream& operator<<(std::ostream& os, const argument& arg)
592 {
593 std::stringstream ss;
594 ss << "names: ";
595 for(const auto& itr : arg.m_names)
596 ss << itr << " ";
597 ss << ", index: " << arg.m_index << ", count: " << arg.m_count
598 << ", min count: " << arg.m_min_count << ", max count: " << arg.m_max_count
599 << ", found: " << std::boolalpha << arg.m_found
600 << ", required: " << std::boolalpha << arg.m_required
601 << ", position: " << arg.m_position << ", values: ";
602 for(const auto& itr : arg.m_values)
603 ss << itr << " ";
604 os << ss.str();
605 return os;
606 }
607
608 friend struct argument_parser;
609 int m_position = Position::IgnoreArgument;
610 int m_count = Count::ANY;
611 int m_min_count = Count::ANY;
612 int m_max_count = Count::ANY;
613 std::vector<std::string> m_names = {};
614 std::string m_desc = {};
615 std::string m_dtype = {};
616 bool m_found = false;
617 bool m_required = false;
618 int m_index = -1;
619 std::type_index m_default_tidx = std::type_index{ typeid(void) };
620 void* m_default = nullptr;
621 callback_t m_callback = [](void*&) {};
622 callback_t m_destroy = [](void*&) {};
623 std::set<std::string> m_choices = {};
624 std::vector<std::string> m_values = {};
625 std::vector<action_func_t> m_actions = {};
626 };
627 //
628 //----------------------------------------------------------------------------------//
629 //
631 : m_desc(std::move(desc))
632 {}
633 //
634 //----------------------------------------------------------------------------------//
635 //
637 {
638 m_arguments.push_back({});
639 m_arguments.back().m_index = static_cast<int>(m_arguments.size()) - 1;
640 return m_arguments.back();
641 }
642 //
643 //----------------------------------------------------------------------------------//
644 //
645 argument& add_argument(const std::initializer_list<std::string>& _names,
646 const std::string& desc, bool req = false)
647 {
648 return add_argument().names(_names).description(desc).required(req);
649 }
650 //
651 //----------------------------------------------------------------------------------//
652 //
653 argument& add_argument(const std::vector<std::string>& _names,
654 const std::string& desc, bool req = false)
655 {
656 return add_argument().names(_names).description(desc).required(req);
657 }
658 //
659 //----------------------------------------------------------------------------------//
660 //
662 {
663 m_positional_arguments.push_back({});
664 auto& _entry = m_positional_arguments.back();
665 _entry.name(_name);
666 _entry.count(1);
667 _entry.m_index = m_positional_arguments.size();
668 return _entry;
669 }
670 //
671 //----------------------------------------------------------------------------------//
672 //
673 template <typename Tp>
674 arg_result get(size_t _idx, Tp& _value)
675 {
676 if(m_positional_values.find(_idx) == m_positional_values.end())
677 return arg_result{ "Positional value not found at index " +
678 std::to_string(_idx) };
679 if(_idx >= m_positional_arguments.size())
680 return arg_result{ "No positional argument was specified for index " +
681 std::to_string(_idx) };
682 _value = m_positional_arguments.at(_idx).get<Tp>();
683 return arg_result{};
684 }
685 //
686 //----------------------------------------------------------------------------------//
687 //
688 template <typename Tp>
689 arg_result get(const std::string& _name, Tp& _value)
690 {
691 // loop over parsed positional args
692 for(size_t i = 0; i < m_positional_values.size(); ++i)
693 {
694 if(i >= m_positional_arguments.size())
695 break;
696
697 // loop over added positional args
698 auto& itr = m_positional_arguments.at(i);
699 for(auto& nitr : itr.m_names)
700 {
701 if(nitr == _name)
702 return get(i, _value);
703 }
704 }
705
706 // not found, check if required
707 for(auto& itr : m_positional_arguments)
708 {
709 for(auto& nitr : itr.m_names)
710 {
711 if(nitr == _name)
712 {
713 if(itr.m_default &&
714 itr.m_default_tidx == std::type_index{ typeid(decay_t<Tp>) })
715 _value = (*static_cast<Tp*>(itr.m_default));
716 else if(itr.m_required)
717 return arg_result{
718 _name + " not parsed from the command line (required)"
719 };
720 return arg_result{};
721 }
722 }
723 }
724
725 return arg_result{ _name + " is not a named positional argument" };
726 }
727 //
728 //----------------------------------------------------------------------------------//
729 //
730 template <typename BoolFuncT, typename ActionFuncT>
731 this_type& add_action(BoolFuncT&& _b, ActionFuncT& _act)
732 {
733 m_actions.push_back(
734 { std::forward<BoolFuncT>(_b), std::forward<ActionFuncT>(_act) });
735 return *this;
736 }
737 //
738 //----------------------------------------------------------------------------------//
739 //
740 template <typename ActionFuncT>
741 this_type& add_action(const std::string& _name, ActionFuncT& _act)
742 {
743 auto _b = [=](this_type& p) { return p.exists(_name); };
744 m_actions.push_back({ _b, std::forward<ActionFuncT>(_act) });
745 return *this;
746 }
747 //
748 //----------------------------------------------------------------------------------//
749 //
750 void print_help(const std::string& _extra = "");
751 //
752 //----------------------------------------------------------------------------------//
753 //
754 /// \fn arg_result parse_known_args(int argc, char** argv, const std::string& delim,
755 /// int verb)
756 /// \param[in,out] argc Number of arguments (i.e. # of command-line args)
757 /// \param[in,out] argv Array of strings (i.e. command-line)
758 /// \param[in] delim Delimiter which separates this argparser's opts from user's
759 /// arguments
760 /// \param[in] verb verbosity
761 ///
762 /// \brief Basic variant of \ref parse_known_args which does not replace argc/argv
763 /// and does not provide an array of strings that it processed
764 ///
765 known_args_t parse_known_args(int argc, char** argv, const std::string& _delim = "--",
766 int verbose_level = 0)
767 {
768 strvec_t _args{};
769 return parse_known_args(argc, argv, _args, _delim, verbose_level);
770 }
771 //
772 //----------------------------------------------------------------------------------//
773 //
774 arg_result parse_known_args(int* argc, char*** argv, const std::string& _delim = "--",
775 int verbose_level = 0)
776 {
777 strvec_t args{};
778 return parse_known_args(argc, argv, args, _delim, verbose_level);
779 }
780 //
781 //----------------------------------------------------------------------------------//
782 //
783 /// \fn arg_result parse_known_args(int* argc, char*** argv, const std::string& delim,
784 /// int verb)
785 /// \param[in,out] argc Pointer to number of arguments (i.e. # of command-line args)
786 /// \param[in,out] argv Pointer to array of strings (i.e. command-line)
787 /// \param[in] delim Delimiter which separates this argparser's opts from user's
788 /// arguments
789 /// \param[in] verb verbosity
790 ///
791 /// \brief This variant calls \ref parse_known_args and replaces argc and argv with
792 /// the argv[0] + anything after delimiter (if the delimiter is provided). If the
793 /// delimiter does not exist, argc and argv are unchanged.
794 ///
795 arg_result parse_known_args(int* argc, char*** argv, strvec_t& _args,
796 const std::string& _delim = "--", int verbose_level = 0);
797 //
798 //----------------------------------------------------------------------------------//
799 //
800 /// \fn arg_result parse_known_args(int argc, char** argv, strvec_t& args, const
801 /// std::string& delim, int verb)
802 /// \param[in,out] argc Number of arguments (i.e. # of command-line args)
803 /// \param[in,out] argv Array of strings (i.e. command-line)
804 /// \param[in,out] args Array of strings processed by this parser
805 /// \param[in] delim Delimiter which separates this argparser's opts from user's
806 /// arguments
807 /// \param[in] verb verbosity
808 ///
809 /// \brief Parses all options until argv[argc-1] or delimiter is found.
810 /// Returns a tuple containing an argument error object (operator bool will return
811 /// true if there was an error) and the new argc and argv after the known arguments
812 /// have been processed. This is slightly different from the Python
813 /// argparse.ArgumentParser.parse_known_args: if the delimiter is not found, it will
814 /// not remove the arguments that it recognizes.
815 /// To distinguish this parsers options from user arguments, use the syntax:
816 ///
817 /// ./<CMD> <PARSER_OPTIONS> -- <USER_ARGS>
818 ///
819 /// And std::get<1>(...) on the return value will be the new argc.
820 /// and std::get<2>(...) on the return value will be the new argv.
821 /// Other valid usages:
822 ///
823 /// ./<CMD> --help (will report this parser's help message)
824 /// ./<CMD> -- --help (will report the applications help message, if supported)
825 /// ./<CMD> <USER_ARGS>
826 /// ./<CMD> <PARSER_OPTIONS>
827 /// ./<CMD> <PARSER_OPTIONS> <USER_ARGS> (intermixed)
828 ///
829 /// will not remove any of the known options.
830 /// In other words, this will remove all arguments after <CMD> until the first "--" if
831 /// reached and everything after the "--" will be placed in argv[1:]
832 ///
834 const std::string& _delim = "--",
835 int verbose_level = 0);
836 //
837 //----------------------------------------------------------------------------------//
838 //
839 template <typename... Args>
840 arg_result parse_args(Args&&... args)
841 {
842 return parse(std::forward<Args>(args)...);
843 }
844 //
845 //----------------------------------------------------------------------------------//
846 //
847 arg_result parse(int argc, char** argv, int verbose_level = 0)
848 {
849 std::vector<std::string> _args;
850 _args.reserve(argc);
851 for(int i = 0; i < argc; ++i)
852 _args.emplace_back((const char*) argv[i]);
853 return parse(_args, verbose_level);
854 }
855 //
856 //----------------------------------------------------------------------------------//
857 //
858 /// \fn arg_result parse(const std::vector<std::string>& args, int verb)
859 /// \param[in] args Array of strings (i.e. command-line arguments)
860 /// \param[in] verb Verbosity
861 ///
862 /// \brief This is the primary function for parsing the command line arguments.
863 /// This is where the map of the options is built and the loop over the
864 /// arguments is performed.
865 arg_result parse(const std::vector<std::string>& _args, int verbose_level = 0);
866 //
867 //----------------------------------------------------------------------------------//
868 //
869 /// \fn argument& enable_help()
870 /// \brief Add a help command
872 {
873 m_help_enabled = true;
874 return add_argument()
875 .names({ "-h", "-?", "--help" })
876 .description("Shows this page")
877 .count(0);
878 }
879 //
880 //----------------------------------------------------------------------------------//
881 //
882 /// \fn bool exists(const std::string& name) const
883 /// \brief Returns whether or not an option was found in the arguments. Only
884 /// useful after a call to \ref parse or \ref parse_known_args.
885 ///
886 /// \code{.cpp}
887 ///
888 /// int main(int argc, char** argv)
889 /// {
890 /// argument_parser p{ argv[0] };
891 /// p.add_argument()
892 /// .names({ "-h", "--help"})
893 /// .description("Help message")
894 /// .count(0);
895 ///
896 /// auto ec = p.parse(argc, argv);
897 /// if(ec)
898 /// {
899 /// std::cerr << "Error: " << ec << std::endl;
900 /// exit(EXIT_FAILURE);
901 /// }
902 ///
903 /// if(p.exists("help"))
904 /// {
905 /// p.print_help();
906 /// exit(EXIT_FAILURE);
907 /// }
908 ///
909 /// // ...
910 /// }
911 /// \endcode
912 bool exists(const std::string& name) const
913 {
914 std::string n = helpers::ltrim(
915 name, [](int c) -> bool { return c != static_cast<int>('-'); });
916 auto itr = m_name_map.find(n);
917 if(itr != m_name_map.end())
918 return m_arguments[static_cast<size_t>(itr->second)].m_found;
919 return false;
920 }
921 //
922 //----------------------------------------------------------------------------------//
923 //
924 /// \fn T get(const std::string& name)
925 /// \tparam T Data type to convert the argument into
926 /// \param[in] name An identifier of the option
927 ///
928 /// \brief Get the value(s) associated with an argument. If option, it should
929 /// be used in conjunction with \ref exists(name). Only useful after a call to \ref
930 /// parse or \ref parse_known_args.
931 ///
932 /// \code{.cpp}
933 ///
934 /// int main(int argc, char** argv)
935 /// {
936 /// argument_parser p{ argv[0] };
937 /// p.add_argument()
938 /// .names({ "-n", "--iterations"})
939 /// .description("Number of iterations")
940 /// .count(1);
941 ///
942 /// // ... etc.
943 ///
944 /// auto nitr = p.get<size_t>("iteration");
945 ///
946 /// // ... etc.
947 /// }
948 /// \endcode
949 template <typename T>
950 T get(const std::string& name)
951 {
952 auto itr = m_name_map.find(name);
953 if(itr != m_name_map.end())
954 return m_arguments[static_cast<size_t>(itr->second)].get<T>();
955 return T{};
956 }
957 //
958 //----------------------------------------------------------------------------------//
959 //
960 int64_t get_count(const std::string& name)
961 {
962 auto itr = m_name_map.find(name);
963 if(itr != m_name_map.end())
964 return m_arguments[static_cast<size_t>(itr->second)].size();
965 return 0;
966 }
967 //
968 //----------------------------------------------------------------------------------//
969 //
970 int64_t get_positional_count() const { return m_positional_values.size(); }
971 //
972 //----------------------------------------------------------------------------------//
973 //
974 static int64_t get_count(argument& a) { return a.m_values.size(); }
975 //
976 //----------------------------------------------------------------------------------//
977 //
978 template <typename ErrorFuncT>
979 void on_error(ErrorFuncT&& _func)
980 {
981 on_error_sfinae(std::forward<ErrorFuncT>(_func), 0);
982 }
983 //
984 //----------------------------------------------------------------------------------//
985 //
986 void set_help_width(int _v) { m_width = _v; }
987
988private:
989 //
990 //----------------------------------------------------------------------------------//
991 //
992 template <typename FuncT = std::function<bool(int, int)>>
993 arg_result check_count(argument& a, const std::string& _do_str = "111",
994 const FuncT& _func = std::not_equal_to<int>{})
995 {
996 int _sz = static_cast<int>(a.m_values.size());
997 int _cnt = a.m_count;
998 int _max = a.m_max_count;
999 int _min = a.m_min_count;
1000
1001 std::bitset<3> _do{ _do_str };
1002 std::bitset<3> _checks;
1003 _checks.reset(); // set all to false
1004 // if <val> > ANY AND <val does not satisfies condition> -> true
1005 _checks.set(0, _do.test(0) && _cnt > argument::Count::ANY && _func(_sz, _cnt));
1006 _checks.set(1, _do.test(1) && _max > argument::Count::ANY && _sz > _max);
1007 _checks.set(2, _do.test(2) && _min > argument::Count::ANY && _sz < _min);
1008 // if no checks failed, return non-error
1009 if(_checks.none())
1010 return arg_result{};
1011 // otherwise, compose an error message
1012 std::stringstream msg;
1013 msg << "Argument: " << a.get_name() << " failed to satisfy its argument count "
1014 << "requirements. Number of arguments: " << _sz << ".";
1015 if(_checks.test(0))
1016 {
1017 msg << "\n[" << a.get_name() << "]> Requires exactly " << _cnt << " values.";
1018 }
1019 else
1020 {
1021 if(_checks.test(1))
1022 {
1023 msg << "\n[" << a.get_name() << "]> Requires less than " << _max + 1
1024 << " values.";
1025 }
1026 if(_checks.test(2))
1027 {
1028 msg << "\n[" << a.get_name() << "]> Requires more than " << _min - 1
1029 << " values.";
1030 }
1031 }
1032 return arg_result(msg.str());
1033 }
1034 //
1035 //----------------------------------------------------------------------------------//
1036 //
1037 template <typename FuncT = std::function<bool(int, int)>>
1038 arg_result check_count(const std::string& name, const std::string& _do = "111",
1039 const FuncT& _func = std::not_equal_to<int>{})
1040 {
1041 auto itr = m_name_map.find(name);
1042 if(itr != m_name_map.end())
1043 return check_count(m_arguments[static_cast<size_t>(itr->second)], _do, _func);
1044 return arg_result{};
1045 }
1046 //
1047 //----------------------------------------------------------------------------------//
1048 //
1049 template <typename ErrorFuncT>
1050 auto on_error_sfinae(ErrorFuncT&& _func, int)
1051 -> decltype(_func(std::declval<this_type&>(), std::declval<result_type>()),
1052 void())
1053 {
1054 m_error_func = std::forward<ErrorFuncT>(_func);
1055 }
1056 //
1057 //----------------------------------------------------------------------------------//
1058 //
1059 template <typename ErrorFuncT>
1060 auto on_error_sfinae(ErrorFuncT&& _func, long)
1061 -> decltype(_func(std::declval<result_type>()), void())
1062 {
1063 auto _wrap_func = [=](this_type&, result_type ret) { _func(ret); };
1064 m_error_func = _wrap_func;
1065 }
1066 //
1067 //----------------------------------------------------------------------------------//
1068 //
1069 arg_result begin_argument(const std::string& arg, bool longarg, int position);
1070 arg_result add_value(const std::string& value, int location);
1071 arg_result end_argument();
1072 //
1073 //----------------------------------------------------------------------------------//
1074 //
1075private:
1076 bool m_help_enabled = false;
1077 int m_current = -1;
1078 int m_width = 30;
1079 std::string m_desc = {};
1080 std::string m_bin = {};
1081 error_func_t m_error_func = [](this_type&, const result_type&) {};
1082 std::vector<argument> m_arguments = {};
1083 std::map<int, int> m_positional_map = {};
1084 std::map<std::string, int> m_name_map = {};
1085 std::vector<action_pair_t> m_actions = {};
1086 std::vector<argument> m_positional_arguments = {};
1087 std::map<int, std::string> m_positional_values = {};
1088};
1089//
1090//--------------------------------------------------------------------------------------//
1091//
1092template <>
1093inline std::string
1094argument_parser::argument::get<std::string>()
1095{
1096 using T = std::string;
1097 if(m_values.empty() && m_default != nullptr &&
1098 m_default_tidx == std::type_index{ typeid(T) })
1099 return (*static_cast<T*>(m_default));
1100 return helpers::join(m_values.begin(), m_values.end());
1101}
1102//
1103//--------------------------------------------------------------------------------------//
1104//
1105template <>
1106inline std::vector<std::string>
1107argument_parser::argument::get<std::vector<std::string>>()
1108{
1109 using T = std::vector<std::string>;
1110 if(m_values.empty() && m_default != nullptr &&
1111 m_default_tidx == std::type_index{ typeid(T) })
1112 return (*static_cast<T*>(m_default));
1113 return m_values;
1114}
1115//
1116//--------------------------------------------------------------------------------------//
1117//
1118} // namespace argparse
1119} // namespace tim
1120
1121#if defined(TIMEMORY_UTILITY_HEADER_MODE)
1123#endif
STL namespace.
::tim::statistics< Tp > max(::tim::statistics< Tp > lhs, const Tp &rhs)
Definition: statistics.hpp:320
typename std::enable_if< B, T >::type enable_if_t
Definition: types.hpp:58
return _hash_map end()
auto join(const char *sep, Arg &&arg, Args &&... args)
Definition: declaration.hpp:74
Definition: kokkosp.cpp:39
std::array< char *, 4 > _args
bool get_bool(const std::string &strbool, bool _default) noexcept
Definition: utility.cpp:75
typename std::decay< T >::type decay_t
Alias template for decay.
Definition: types.hpp:194
typename std::enable_if< B, T >::type enable_if_t
Alias template for enable_if.
Definition: types.hpp:190
char ** argv
Definition: config.cpp:55
tim::mpl::apply< std::string > string
Definition: macros.hpp:53
const std::string std::ostream * os
description("A generic option for any setting. Each argument MUST be passed in " "form: 'NAME=VALUE'. E.g. --timemory-args " "\"papi_events=PAPI_TOT_INS,PAPI_TOT_CYC\" text_output=off") .action([&](parser_t &p)
Definition: config.cpp:312
friend std::ostream & operator<<(std::ostream &os, const arg_result &dt)
Definition: argparse.cpp:697
const std::string & what() const
Definition: argparse.hpp:351
arg_result(std::string err) noexcept
Definition: argparse.hpp:342
friend std::ostream & operator<<(std::ostream &os, const argument &arg)
Definition: argparse.hpp:591
argument & position(int position)
Definition: argparse.hpp:408
argument & set_default(const T &val)
Definition: argparse.hpp:442
std::enable_if_t< std::is_same< T, bool >::value, T > get()
Definition: argparse.hpp:535
argument & description(const std::string &description)
Definition: argparse.hpp:390
std::enable_if_t< !helpers::is_container< T >::value &&!std::is_same< T, bool >::value, T > get()
Definition: argparse.hpp:523
argument & choices(const ContainerT< T, ExtraT... > &_choices)
Definition: argparse.hpp:483
argument & dtype(const std::string &_dtype)
Definition: argparse.hpp:396
std::function< void(void *&)> callback_t
Definition: argparse.hpp:362
argument & name(const std::string &name)
Definition: argparse.hpp:377
argument & action(ActionFuncT &&_func)
Definition: argparse.hpp:495
argument & names(const std::vector< std::string > &names)
Definition: argparse.hpp:383
argument & choices(const std::initializer_list< T > &_choices)
Definition: argparse.hpp:469
std::enable_if_t< helpers::is_container< T >::value, T > get()
Definition: argparse.hpp:504
void on_error(ErrorFuncT &&_func)
Definition: argparse.hpp:979
bool exists(const std::string &name) const
Returns whether or not an option was found in the arguments. Only useful after a call to parse or par...
Definition: argparse.hpp:912
int64_t get_positional_count() const
Definition: argparse.hpp:970
std::pair< bool_func_t, action_func_t > action_pair_t
Definition: argparse.hpp:331
known_args_t parse_known_args(int argc, char **argv, const std::string &_delim="--", int verbose_level=0)
Basic variant of parse_known_args which does not replace argc/argv and does not provide an array of s...
Definition: argparse.hpp:765
argument & add_argument(const std::initializer_list< std::string > &_names, const std::string &desc, bool req=false)
Definition: argparse.hpp:645
int64_t get_count(const std::string &name)
Definition: argparse.hpp:960
this_type & add_action(const std::string &_name, ActionFuncT &_act)
Definition: argparse.hpp:741
arg_result get(const std::string &_name, Tp &_value)
Definition: argparse.hpp:689
arg_result get(size_t _idx, Tp &_value)
Definition: argparse.hpp:674
argument & add_positional_argument(const std::string &_name)
Definition: argparse.hpp:661
std::tuple< arg_result, int, char ** > known_args_t
Definition: argparse.hpp:333
this_type & add_action(BoolFuncT &&_b, ActionFuncT &_act)
Definition: argparse.hpp:731
arg_result parse(int argc, char **argv, int verbose_level=0)
Definition: argparse.hpp:847
void print_help(const std::string &_extra="")
Definition: argparse.cpp:109
argument & enable_help()
Add a help command.
Definition: argparse.hpp:871
static int64_t get_count(argument &a)
Definition: argparse.hpp:974
std::set< std::string > strset_t
Definition: argparse.hpp:335
std::function< bool(this_type &)> bool_func_t
Definition: argparse.hpp:329
std::vector< std::string > strvec_t
Definition: argparse.hpp:334
T get(const std::string &name)
Get the value(s) associated with an argument. If option, it should be used in conjunction with exists...
Definition: argparse.hpp:950
argument_parser(std::string desc)
Definition: argparse.hpp:630
arg_result parse_known_args(int *argc, char ***argv, const std::string &_delim="--", int verbose_level=0)
This variant calls parse_known_args and replaces argc and argv with the argv[0] + anything after deli...
Definition: argparse.hpp:774
arg_result parse_args(Args &&... args)
Definition: argparse.hpp:840
std::function< void(this_type &, arg_result &)> error_func_t
Definition: argparse.hpp:332
std::function< void(this_type &)> action_func_t
Definition: argparse.hpp:330
argument & add_argument(const std::vector< std::string > &_names, const std::string &desc, bool req=false)
Definition: argparse.hpp:653
std::tuple< int, char **, std::string > base_type
Definition: argparse.hpp:273
This class exists to simplify creating argument arrays compatible with execv* routines and MPI_Comm_s...
Definition: argparse.hpp:270
static void free_execv(cargs_t &itr)
Definition: argparse.hpp:314
argument_vector(Args &&... args)
Definition: argparse.hpp:300
cargs_t get_execv(const base_type &_prepend, size_t _beg=0, size_t _end=std::numeric_limits< size_t >::max()) const
Definition: argparse.cpp:65
std::vector< std::string > base_type
Definition: argparse.hpp:296
char * strdup(const char *s)
Definition: timemory_c.c:41