Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
TensorSpeech
GitHub Repository: TensorSpeech/TensorFlowTTS
Path: blob/master/examples/cppwin/TensorflowTTSCppInference/ext/cxxopts.hpp
1564 views
1
/*
2
3
Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck
4
5
Permission is hereby granted, free of charge, to any person obtaining a copy
6
of this software and associated documentation files (the "Software"), to deal
7
in the Software without restriction, including without limitation the rights
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
copies of the Software, and to permit persons to whom the Software is
10
furnished to do so, subject to the following conditions:
11
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
14
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
THE SOFTWARE.
22
23
*/
24
25
#ifndef CXXOPTS_HPP_INCLUDED
26
#define CXXOPTS_HPP_INCLUDED
27
28
#include <cstring>
29
#include <cctype>
30
#include <exception>
31
#include <iostream>
32
#include <limits>
33
#include <map>
34
#include <memory>
35
#include <regex>
36
#include <sstream>
37
#include <string>
38
#include <unordered_map>
39
#include <unordered_set>
40
#include <vector>
41
42
#ifdef __cpp_lib_optional
43
#include <optional>
44
#define CXXOPTS_HAS_OPTIONAL
45
#endif
46
47
#ifndef CXXOPTS_VECTOR_DELIMITER
48
#define CXXOPTS_VECTOR_DELIMITER ','
49
#endif
50
51
#define CXXOPTS__VERSION_MAJOR 2
52
#define CXXOPTS__VERSION_MINOR 2
53
#define CXXOPTS__VERSION_PATCH 0
54
55
namespace cxxopts
56
{
57
static constexpr struct {
58
uint8_t major, minor, patch;
59
} version = {
60
CXXOPTS__VERSION_MAJOR,
61
CXXOPTS__VERSION_MINOR,
62
CXXOPTS__VERSION_PATCH
63
};
64
}
65
66
//when we ask cxxopts to use Unicode, help strings are processed using ICU,
67
//which results in the correct lengths being computed for strings when they
68
//are formatted for the help output
69
//it is necessary to make sure that <unicode/unistr.h> can be found by the
70
//compiler, and that icu-uc is linked in to the binary.
71
72
#ifdef CXXOPTS_USE_UNICODE
73
#include <unicode/unistr.h>
74
75
namespace cxxopts
76
{
77
typedef icu::UnicodeString String;
78
79
inline
80
String
81
toLocalString(std::string s)
82
{
83
return icu::UnicodeString::fromUTF8(std::move(s));
84
}
85
86
class UnicodeStringIterator : public
87
std::iterator<std::forward_iterator_tag, int32_t>
88
{
89
public:
90
91
UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos)
92
: s(string)
93
, i(pos)
94
{
95
}
96
97
value_type
98
operator*() const
99
{
100
return s->char32At(i);
101
}
102
103
bool
104
operator==(const UnicodeStringIterator& rhs) const
105
{
106
return s == rhs.s && i == rhs.i;
107
}
108
109
bool
110
operator!=(const UnicodeStringIterator& rhs) const
111
{
112
return !(*this == rhs);
113
}
114
115
UnicodeStringIterator&
116
operator++()
117
{
118
++i;
119
return *this;
120
}
121
122
UnicodeStringIterator
123
operator+(int32_t v)
124
{
125
return UnicodeStringIterator(s, i + v);
126
}
127
128
private:
129
const icu::UnicodeString* s;
130
int32_t i;
131
};
132
133
inline
134
String&
135
stringAppend(String&s, String a)
136
{
137
return s.append(std::move(a));
138
}
139
140
inline
141
String&
142
stringAppend(String& s, int n, UChar32 c)
143
{
144
for (int i = 0; i != n; ++i)
145
{
146
s.append(c);
147
}
148
149
return s;
150
}
151
152
template <typename Iterator>
153
String&
154
stringAppend(String& s, Iterator begin, Iterator end)
155
{
156
while (begin != end)
157
{
158
s.append(*begin);
159
++begin;
160
}
161
162
return s;
163
}
164
165
inline
166
size_t
167
stringLength(const String& s)
168
{
169
return s.length();
170
}
171
172
inline
173
std::string
174
toUTF8String(const String& s)
175
{
176
std::string result;
177
s.toUTF8String(result);
178
179
return result;
180
}
181
182
inline
183
bool
184
empty(const String& s)
185
{
186
return s.isEmpty();
187
}
188
}
189
190
namespace std
191
{
192
inline
193
cxxopts::UnicodeStringIterator
194
begin(const icu::UnicodeString& s)
195
{
196
return cxxopts::UnicodeStringIterator(&s, 0);
197
}
198
199
inline
200
cxxopts::UnicodeStringIterator
201
end(const icu::UnicodeString& s)
202
{
203
return cxxopts::UnicodeStringIterator(&s, s.length());
204
}
205
}
206
207
//ifdef CXXOPTS_USE_UNICODE
208
#else
209
210
namespace cxxopts
211
{
212
typedef std::string String;
213
214
template <typename T>
215
T
216
toLocalString(T&& t)
217
{
218
return std::forward<T>(t);
219
}
220
221
inline
222
size_t
223
stringLength(const String& s)
224
{
225
return s.length();
226
}
227
228
inline
229
String&
230
stringAppend(String&s, String a)
231
{
232
return s.append(std::move(a));
233
}
234
235
inline
236
String&
237
stringAppend(String& s, size_t n, char c)
238
{
239
return s.append(n, c);
240
}
241
242
template <typename Iterator>
243
String&
244
stringAppend(String& s, Iterator begin, Iterator end)
245
{
246
return s.append(begin, end);
247
}
248
249
template <typename T>
250
std::string
251
toUTF8String(T&& t)
252
{
253
return std::forward<T>(t);
254
}
255
256
inline
257
bool
258
empty(const std::string& s)
259
{
260
return s.empty();
261
}
262
}
263
264
//ifdef CXXOPTS_USE_UNICODE
265
#endif
266
267
namespace cxxopts
268
{
269
namespace
270
{
271
#ifdef _WIN32
272
const std::string LQUOTE("\'");
273
const std::string RQUOTE("\'");
274
#else
275
const std::string LQUOTE("‘");
276
const std::string RQUOTE("’");
277
#endif
278
}
279
280
class Value : public std::enable_shared_from_this<Value>
281
{
282
public:
283
284
virtual ~Value() = default;
285
286
virtual
287
std::shared_ptr<Value>
288
clone() const = 0;
289
290
virtual void
291
parse(const std::string& text) const = 0;
292
293
virtual void
294
parse() const = 0;
295
296
virtual bool
297
has_default() const = 0;
298
299
virtual bool
300
is_container() const = 0;
301
302
virtual bool
303
has_implicit() const = 0;
304
305
virtual std::string
306
get_default_value() const = 0;
307
308
virtual std::string
309
get_implicit_value() const = 0;
310
311
virtual std::shared_ptr<Value>
312
default_value(const std::string& value) = 0;
313
314
virtual std::shared_ptr<Value>
315
implicit_value(const std::string& value) = 0;
316
317
virtual std::shared_ptr<Value>
318
no_implicit_value() = 0;
319
320
virtual bool
321
is_boolean() const = 0;
322
};
323
324
class OptionException : public std::exception
325
{
326
public:
327
OptionException(const std::string& message)
328
: m_message(message)
329
{
330
}
331
332
virtual const char*
333
what() const noexcept
334
{
335
return m_message.c_str();
336
}
337
338
private:
339
std::string m_message;
340
};
341
342
class OptionSpecException : public OptionException
343
{
344
public:
345
346
OptionSpecException(const std::string& message)
347
: OptionException(message)
348
{
349
}
350
};
351
352
class OptionParseException : public OptionException
353
{
354
public:
355
OptionParseException(const std::string& message)
356
: OptionException(message)
357
{
358
}
359
};
360
361
class option_exists_error : public OptionSpecException
362
{
363
public:
364
option_exists_error(const std::string& option)
365
: OptionSpecException("Option " + LQUOTE + option + RQUOTE + " already exists")
366
{
367
}
368
};
369
370
class invalid_option_format_error : public OptionSpecException
371
{
372
public:
373
invalid_option_format_error(const std::string& format)
374
: OptionSpecException("Invalid option format " + LQUOTE + format + RQUOTE)
375
{
376
}
377
};
378
379
class option_syntax_exception : public OptionParseException {
380
public:
381
option_syntax_exception(const std::string& text)
382
: OptionParseException("Argument " + LQUOTE + text + RQUOTE +
383
" starts with a - but has incorrect syntax")
384
{
385
}
386
};
387
388
class option_not_exists_exception : public OptionParseException
389
{
390
public:
391
option_not_exists_exception(const std::string& option)
392
: OptionParseException("Option " + LQUOTE + option + RQUOTE + " does not exist")
393
{
394
}
395
};
396
397
class missing_argument_exception : public OptionParseException
398
{
399
public:
400
missing_argument_exception(const std::string& option)
401
: OptionParseException(
402
"Option " + LQUOTE + option + RQUOTE + " is missing an argument"
403
)
404
{
405
}
406
};
407
408
class option_requires_argument_exception : public OptionParseException
409
{
410
public:
411
option_requires_argument_exception(const std::string& option)
412
: OptionParseException(
413
"Option " + LQUOTE + option + RQUOTE + " requires an argument"
414
)
415
{
416
}
417
};
418
419
class option_not_has_argument_exception : public OptionParseException
420
{
421
public:
422
option_not_has_argument_exception
423
(
424
const std::string& option,
425
const std::string& arg
426
)
427
: OptionParseException(
428
"Option " + LQUOTE + option + RQUOTE +
429
" does not take an argument, but argument " +
430
LQUOTE + arg + RQUOTE + " given"
431
)
432
{
433
}
434
};
435
436
class option_not_present_exception : public OptionParseException
437
{
438
public:
439
option_not_present_exception(const std::string& option)
440
: OptionParseException("Option " + LQUOTE + option + RQUOTE + " not present")
441
{
442
}
443
};
444
445
class argument_incorrect_type : public OptionParseException
446
{
447
public:
448
argument_incorrect_type
449
(
450
const std::string& arg
451
)
452
: OptionParseException(
453
"Argument " + LQUOTE + arg + RQUOTE + " failed to parse"
454
)
455
{
456
}
457
};
458
459
class option_required_exception : public OptionParseException
460
{
461
public:
462
option_required_exception(const std::string& option)
463
: OptionParseException(
464
"Option " + LQUOTE + option + RQUOTE + " is required but not present"
465
)
466
{
467
}
468
};
469
470
namespace values
471
{
472
namespace
473
{
474
std::basic_regex<char> integer_pattern
475
("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)");
476
std::basic_regex<char> truthy_pattern
477
("(t|T)(rue)?|1");
478
std::basic_regex<char> falsy_pattern
479
("(f|F)(alse)?|0");
480
}
481
482
namespace detail
483
{
484
template <typename T, bool B>
485
struct SignedCheck;
486
487
template <typename T>
488
struct SignedCheck<T, true>
489
{
490
template <typename U>
491
void
492
operator()(bool negative, U u, const std::string& text)
493
{
494
if (negative)
495
{
496
if (u > static_cast<U>((std::numeric_limits<T>::min)()))
497
{
498
throw argument_incorrect_type(text);
499
}
500
}
501
else
502
{
503
if (u > static_cast<U>((std::numeric_limits<T>::max)()))
504
{
505
throw argument_incorrect_type(text);
506
}
507
}
508
}
509
};
510
511
template <typename T>
512
struct SignedCheck<T, false>
513
{
514
template <typename U>
515
void
516
operator()(bool, U, const std::string&) {}
517
};
518
519
template <typename T, typename U>
520
void
521
check_signed_range(bool negative, U value, const std::string& text)
522
{
523
SignedCheck<T, std::numeric_limits<T>::is_signed>()(negative, value, text);
524
}
525
}
526
527
template <typename R, typename T>
528
R
529
checked_negate(T&& t, const std::string&, std::true_type)
530
{
531
// if we got to here, then `t` is a positive number that fits into
532
// `R`. So to avoid MSVC C4146, we first cast it to `R`.
533
// See https://github.com/jarro2783/cxxopts/issues/62 for more details.
534
return -static_cast<R>(t-1)-1;
535
}
536
537
template <typename R, typename T>
538
T
539
checked_negate(T&&, const std::string& text, std::false_type)
540
{
541
throw argument_incorrect_type(text);
542
}
543
544
template <typename T>
545
void
546
integer_parser(const std::string& text, T& value)
547
{
548
std::smatch match;
549
std::regex_match(text, match, integer_pattern);
550
551
if (match.length() == 0)
552
{
553
throw argument_incorrect_type(text);
554
}
555
556
if (match.length(4) > 0)
557
{
558
value = 0;
559
return;
560
}
561
562
using US = typename std::make_unsigned<T>::type;
563
564
constexpr bool is_signed = std::numeric_limits<T>::is_signed;
565
const bool negative = match.length(1) > 0;
566
const uint8_t base = match.length(2) > 0 ? 16 : 10;
567
568
auto value_match = match[3];
569
570
US result = 0;
571
572
for (auto iter = value_match.first; iter != value_match.second; ++iter)
573
{
574
US digit = 0;
575
576
if (*iter >= '0' && *iter <= '9')
577
{
578
digit = static_cast<US>(*iter - '0');
579
}
580
else if (base == 16 && *iter >= 'a' && *iter <= 'f')
581
{
582
digit = static_cast<US>(*iter - 'a' + 10);
583
}
584
else if (base == 16 && *iter >= 'A' && *iter <= 'F')
585
{
586
digit = static_cast<US>(*iter - 'A' + 10);
587
}
588
else
589
{
590
throw argument_incorrect_type(text);
591
}
592
593
US next = result * base + digit;
594
if (result > next)
595
{
596
throw argument_incorrect_type(text);
597
}
598
599
result = next;
600
}
601
602
detail::check_signed_range<T>(negative, result, text);
603
604
if (negative)
605
{
606
value = checked_negate<T>(result,
607
text,
608
std::integral_constant<bool, is_signed>());
609
}
610
else
611
{
612
value = static_cast<T>(result);
613
}
614
}
615
616
template <typename T>
617
void stringstream_parser(const std::string& text, T& value)
618
{
619
std::stringstream in(text);
620
in >> value;
621
if (!in) {
622
throw argument_incorrect_type(text);
623
}
624
}
625
626
inline
627
void
628
parse_value(const std::string& text, uint8_t& value)
629
{
630
integer_parser(text, value);
631
}
632
633
inline
634
void
635
parse_value(const std::string& text, int8_t& value)
636
{
637
integer_parser(text, value);
638
}
639
640
inline
641
void
642
parse_value(const std::string& text, uint16_t& value)
643
{
644
integer_parser(text, value);
645
}
646
647
inline
648
void
649
parse_value(const std::string& text, int16_t& value)
650
{
651
integer_parser(text, value);
652
}
653
654
inline
655
void
656
parse_value(const std::string& text, uint32_t& value)
657
{
658
integer_parser(text, value);
659
}
660
661
inline
662
void
663
parse_value(const std::string& text, int32_t& value)
664
{
665
integer_parser(text, value);
666
}
667
668
inline
669
void
670
parse_value(const std::string& text, uint64_t& value)
671
{
672
integer_parser(text, value);
673
}
674
675
inline
676
void
677
parse_value(const std::string& text, int64_t& value)
678
{
679
integer_parser(text, value);
680
}
681
682
inline
683
void
684
parse_value(const std::string& text, bool& value)
685
{
686
std::smatch result;
687
std::regex_match(text, result, truthy_pattern);
688
689
if (!result.empty())
690
{
691
value = true;
692
return;
693
}
694
695
std::regex_match(text, result, falsy_pattern);
696
if (!result.empty())
697
{
698
value = false;
699
return;
700
}
701
702
throw argument_incorrect_type(text);
703
}
704
705
inline
706
void
707
parse_value(const std::string& text, std::string& value)
708
{
709
value = text;
710
}
711
712
// The fallback parser. It uses the stringstream parser to parse all types
713
// that have not been overloaded explicitly. It has to be placed in the
714
// source code before all other more specialized templates.
715
template <typename T>
716
void
717
parse_value(const std::string& text, T& value) {
718
stringstream_parser(text, value);
719
}
720
721
template <typename T>
722
void
723
parse_value(const std::string& text, std::vector<T>& value)
724
{
725
std::stringstream in(text);
726
std::string token;
727
while(in.eof() == false && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) {
728
T v;
729
parse_value(token, v);
730
value.emplace_back(std::move(v));
731
}
732
}
733
734
#ifdef CXXOPTS_HAS_OPTIONAL
735
template <typename T>
736
void
737
parse_value(const std::string& text, std::optional<T>& value)
738
{
739
T result;
740
parse_value(text, result);
741
value = std::move(result);
742
}
743
#endif
744
745
template <typename T>
746
struct type_is_container
747
{
748
static constexpr bool value = false;
749
};
750
751
template <typename T>
752
struct type_is_container<std::vector<T>>
753
{
754
static constexpr bool value = true;
755
};
756
757
template <typename T>
758
class abstract_value : public Value
759
{
760
using Self = abstract_value<T>;
761
762
public:
763
abstract_value()
764
: m_result(std::make_shared<T>())
765
, m_store(m_result.get())
766
{
767
}
768
769
abstract_value(T* t)
770
: m_store(t)
771
{
772
}
773
774
virtual ~abstract_value() = default;
775
776
abstract_value(const abstract_value& rhs)
777
{
778
if (rhs.m_result)
779
{
780
m_result = std::make_shared<T>();
781
m_store = m_result.get();
782
}
783
else
784
{
785
m_store = rhs.m_store;
786
}
787
788
m_default = rhs.m_default;
789
m_implicit = rhs.m_implicit;
790
m_default_value = rhs.m_default_value;
791
m_implicit_value = rhs.m_implicit_value;
792
}
793
794
void
795
parse(const std::string& text) const
796
{
797
parse_value(text, *m_store);
798
}
799
800
bool
801
is_container() const
802
{
803
return type_is_container<T>::value;
804
}
805
806
void
807
parse() const
808
{
809
parse_value(m_default_value, *m_store);
810
}
811
812
bool
813
has_default() const
814
{
815
return m_default;
816
}
817
818
bool
819
has_implicit() const
820
{
821
return m_implicit;
822
}
823
824
std::shared_ptr<Value>
825
default_value(const std::string& value)
826
{
827
m_default = true;
828
m_default_value = value;
829
return shared_from_this();
830
}
831
832
std::shared_ptr<Value>
833
implicit_value(const std::string& value)
834
{
835
m_implicit = true;
836
m_implicit_value = value;
837
return shared_from_this();
838
}
839
840
std::shared_ptr<Value>
841
no_implicit_value()
842
{
843
m_implicit = false;
844
return shared_from_this();
845
}
846
847
std::string
848
get_default_value() const
849
{
850
return m_default_value;
851
}
852
853
std::string
854
get_implicit_value() const
855
{
856
return m_implicit_value;
857
}
858
859
bool
860
is_boolean() const
861
{
862
return std::is_same<T, bool>::value;
863
}
864
865
const T&
866
get() const
867
{
868
if (m_store == nullptr)
869
{
870
return *m_result;
871
}
872
else
873
{
874
return *m_store;
875
}
876
}
877
878
protected:
879
std::shared_ptr<T> m_result;
880
T* m_store;
881
882
bool m_default = false;
883
bool m_implicit = false;
884
885
std::string m_default_value;
886
std::string m_implicit_value;
887
};
888
889
template <typename T>
890
class standard_value : public abstract_value<T>
891
{
892
public:
893
using abstract_value<T>::abstract_value;
894
895
std::shared_ptr<Value>
896
clone() const
897
{
898
return std::make_shared<standard_value<T>>(*this);
899
}
900
};
901
902
template <>
903
class standard_value<bool> : public abstract_value<bool>
904
{
905
public:
906
~standard_value() = default;
907
908
standard_value()
909
{
910
set_default_and_implicit();
911
}
912
913
standard_value(bool* b)
914
: abstract_value(b)
915
{
916
set_default_and_implicit();
917
}
918
919
std::shared_ptr<Value>
920
clone() const
921
{
922
return std::make_shared<standard_value<bool>>(*this);
923
}
924
925
private:
926
927
void
928
set_default_and_implicit()
929
{
930
m_default = true;
931
m_default_value = "false";
932
m_implicit = true;
933
m_implicit_value = "true";
934
}
935
};
936
}
937
938
template <typename T>
939
std::shared_ptr<Value>
940
value()
941
{
942
return std::make_shared<values::standard_value<T>>();
943
}
944
945
template <typename T>
946
std::shared_ptr<Value>
947
value(T& t)
948
{
949
return std::make_shared<values::standard_value<T>>(&t);
950
}
951
952
class OptionAdder;
953
954
class OptionDetails
955
{
956
public:
957
OptionDetails
958
(
959
const std::string& short_,
960
const std::string& long_,
961
const String& desc,
962
std::shared_ptr<const Value> val
963
)
964
: m_short(short_)
965
, m_long(long_)
966
, m_desc(desc)
967
, m_value(val)
968
, m_count(0)
969
{
970
}
971
972
OptionDetails(const OptionDetails& rhs)
973
: m_desc(rhs.m_desc)
974
, m_count(rhs.m_count)
975
{
976
m_value = rhs.m_value->clone();
977
}
978
979
OptionDetails(OptionDetails&& rhs) = default;
980
981
const String&
982
description() const
983
{
984
return m_desc;
985
}
986
987
const Value& value() const {
988
return *m_value;
989
}
990
991
std::shared_ptr<Value>
992
make_storage() const
993
{
994
return m_value->clone();
995
}
996
997
const std::string&
998
short_name() const
999
{
1000
return m_short;
1001
}
1002
1003
const std::string&
1004
long_name() const
1005
{
1006
return m_long;
1007
}
1008
1009
private:
1010
std::string m_short;
1011
std::string m_long;
1012
String m_desc;
1013
std::shared_ptr<const Value> m_value;
1014
int m_count;
1015
};
1016
1017
struct HelpOptionDetails
1018
{
1019
std::string s;
1020
std::string l;
1021
String desc;
1022
bool has_default;
1023
std::string default_value;
1024
bool has_implicit;
1025
std::string implicit_value;
1026
std::string arg_help;
1027
bool is_container;
1028
bool is_boolean;
1029
};
1030
1031
struct HelpGroupDetails
1032
{
1033
std::string name;
1034
std::string description;
1035
std::vector<HelpOptionDetails> options;
1036
};
1037
1038
class OptionValue
1039
{
1040
public:
1041
void
1042
parse
1043
(
1044
std::shared_ptr<const OptionDetails> details,
1045
const std::string& text
1046
)
1047
{
1048
ensure_value(details);
1049
++m_count;
1050
m_value->parse(text);
1051
}
1052
1053
void
1054
parse_default(std::shared_ptr<const OptionDetails> details)
1055
{
1056
ensure_value(details);
1057
m_default = true;
1058
m_value->parse();
1059
}
1060
1061
size_t
1062
count() const noexcept
1063
{
1064
return m_count;
1065
}
1066
1067
// TODO: maybe default options should count towards the number of arguments
1068
bool
1069
has_default() const noexcept
1070
{
1071
return m_default;
1072
}
1073
1074
template <typename T>
1075
const T&
1076
as() const
1077
{
1078
if (m_value == nullptr) {
1079
throw std::domain_error("No value");
1080
}
1081
1082
#ifdef CXXOPTS_NO_RTTI
1083
return static_cast<const values::standard_value<T>&>(*m_value).get();
1084
#else
1085
return dynamic_cast<const values::standard_value<T>&>(*m_value).get();
1086
#endif
1087
}
1088
1089
private:
1090
void
1091
ensure_value(std::shared_ptr<const OptionDetails> details)
1092
{
1093
if (m_value == nullptr)
1094
{
1095
m_value = details->make_storage();
1096
}
1097
}
1098
1099
std::shared_ptr<Value> m_value;
1100
size_t m_count = 0;
1101
bool m_default = false;
1102
};
1103
1104
class KeyValue
1105
{
1106
public:
1107
KeyValue(std::string key_, std::string value_)
1108
: m_key(std::move(key_))
1109
, m_value(std::move(value_))
1110
{
1111
}
1112
1113
const
1114
std::string&
1115
key() const
1116
{
1117
return m_key;
1118
}
1119
1120
const
1121
std::string&
1122
value() const
1123
{
1124
return m_value;
1125
}
1126
1127
template <typename T>
1128
T
1129
as() const
1130
{
1131
T result;
1132
values::parse_value(m_value, result);
1133
return result;
1134
}
1135
1136
private:
1137
std::string m_key;
1138
std::string m_value;
1139
};
1140
1141
class ParseResult
1142
{
1143
public:
1144
1145
ParseResult(
1146
const std::shared_ptr<
1147
std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1148
>,
1149
std::vector<std::string>,
1150
bool allow_unrecognised,
1151
int&, char**&);
1152
1153
size_t
1154
count(const std::string& o) const
1155
{
1156
auto iter = m_options->find(o);
1157
if (iter == m_options->end())
1158
{
1159
return 0;
1160
}
1161
1162
auto riter = m_results.find(iter->second);
1163
1164
return riter->second.count();
1165
}
1166
1167
const OptionValue&
1168
operator[](const std::string& option) const
1169
{
1170
auto iter = m_options->find(option);
1171
1172
if (iter == m_options->end())
1173
{
1174
throw option_not_present_exception(option);
1175
}
1176
1177
auto riter = m_results.find(iter->second);
1178
1179
return riter->second;
1180
}
1181
1182
const std::vector<KeyValue>&
1183
arguments() const
1184
{
1185
return m_sequential;
1186
}
1187
1188
private:
1189
1190
void
1191
parse(int& argc, char**& argv);
1192
1193
void
1194
add_to_option(const std::string& option, const std::string& arg);
1195
1196
bool
1197
consume_positional(std::string a);
1198
1199
void
1200
parse_option
1201
(
1202
std::shared_ptr<OptionDetails> value,
1203
const std::string& name,
1204
const std::string& arg = ""
1205
);
1206
1207
void
1208
parse_default(std::shared_ptr<OptionDetails> details);
1209
1210
void
1211
checked_parse_arg
1212
(
1213
int argc,
1214
char* argv[],
1215
int& current,
1216
std::shared_ptr<OptionDetails> value,
1217
const std::string& name
1218
);
1219
1220
const std::shared_ptr<
1221
std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1222
> m_options;
1223
std::vector<std::string> m_positional;
1224
std::vector<std::string>::iterator m_next_positional;
1225
std::unordered_set<std::string> m_positional_set;
1226
std::unordered_map<std::shared_ptr<OptionDetails>, OptionValue> m_results;
1227
1228
bool m_allow_unrecognised;
1229
1230
std::vector<KeyValue> m_sequential;
1231
};
1232
1233
class Options
1234
{
1235
typedef std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1236
OptionMap;
1237
public:
1238
1239
Options(std::string program, std::string help_string = "")
1240
: m_program(std::move(program))
1241
, m_help_string(toLocalString(std::move(help_string)))
1242
, m_custom_help("[OPTION...]")
1243
, m_positional_help("positional parameters")
1244
, m_show_positional(false)
1245
, m_allow_unrecognised(false)
1246
, m_options(std::make_shared<OptionMap>())
1247
, m_next_positional(m_positional.end())
1248
{
1249
}
1250
1251
Options&
1252
positional_help(std::string help_text)
1253
{
1254
m_positional_help = std::move(help_text);
1255
return *this;
1256
}
1257
1258
Options&
1259
custom_help(std::string help_text)
1260
{
1261
m_custom_help = std::move(help_text);
1262
return *this;
1263
}
1264
1265
Options&
1266
show_positional_help()
1267
{
1268
m_show_positional = true;
1269
return *this;
1270
}
1271
1272
Options&
1273
allow_unrecognised_options()
1274
{
1275
m_allow_unrecognised = true;
1276
return *this;
1277
}
1278
1279
ParseResult
1280
parse(int& argc, char**& argv);
1281
1282
OptionAdder
1283
add_options(std::string group = "");
1284
1285
void
1286
add_option
1287
(
1288
const std::string& group,
1289
const std::string& s,
1290
const std::string& l,
1291
std::string desc,
1292
std::shared_ptr<const Value> value,
1293
std::string arg_help
1294
);
1295
1296
//parse positional arguments into the given option
1297
void
1298
parse_positional(std::string option);
1299
1300
void
1301
parse_positional(std::vector<std::string> options);
1302
1303
void
1304
parse_positional(std::initializer_list<std::string> options);
1305
1306
template <typename Iterator>
1307
void
1308
parse_positional(Iterator begin, Iterator end) {
1309
parse_positional(std::vector<std::string>{begin, end});
1310
}
1311
1312
std::string
1313
help(const std::vector<std::string>& groups = {}) const;
1314
1315
const std::vector<std::string>
1316
groups() const;
1317
1318
const HelpGroupDetails&
1319
group_help(const std::string& group) const;
1320
1321
private:
1322
1323
void
1324
add_one_option
1325
(
1326
const std::string& option,
1327
std::shared_ptr<OptionDetails> details
1328
);
1329
1330
String
1331
help_one_group(const std::string& group) const;
1332
1333
void
1334
generate_group_help
1335
(
1336
String& result,
1337
const std::vector<std::string>& groups
1338
) const;
1339
1340
void
1341
generate_all_groups_help(String& result) const;
1342
1343
std::string m_program;
1344
String m_help_string;
1345
std::string m_custom_help;
1346
std::string m_positional_help;
1347
bool m_show_positional;
1348
bool m_allow_unrecognised;
1349
1350
std::shared_ptr<OptionMap> m_options;
1351
std::vector<std::string> m_positional;
1352
std::vector<std::string>::iterator m_next_positional;
1353
std::unordered_set<std::string> m_positional_set;
1354
1355
//mapping from groups to help options
1356
std::map<std::string, HelpGroupDetails> m_help;
1357
};
1358
1359
class OptionAdder
1360
{
1361
public:
1362
1363
OptionAdder(Options& options, std::string group)
1364
: m_options(options), m_group(std::move(group))
1365
{
1366
}
1367
1368
OptionAdder&
1369
operator()
1370
(
1371
const std::string& opts,
1372
const std::string& desc,
1373
std::shared_ptr<const Value> value
1374
= ::cxxopts::value<bool>(),
1375
std::string arg_help = ""
1376
);
1377
1378
private:
1379
Options& m_options;
1380
std::string m_group;
1381
};
1382
1383
namespace
1384
{
1385
constexpr int OPTION_LONGEST = 30;
1386
constexpr int OPTION_DESC_GAP = 2;
1387
1388
std::basic_regex<char> option_matcher
1389
("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)");
1390
1391
std::basic_regex<char> option_specifier
1392
("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
1393
1394
String
1395
format_option
1396
(
1397
const HelpOptionDetails& o
1398
)
1399
{
1400
auto& s = o.s;
1401
auto& l = o.l;
1402
1403
String result = " ";
1404
1405
if (s.size() > 0)
1406
{
1407
result += "-" + toLocalString(s) + ",";
1408
}
1409
else
1410
{
1411
result += " ";
1412
}
1413
1414
if (l.size() > 0)
1415
{
1416
result += " --" + toLocalString(l);
1417
}
1418
1419
auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg";
1420
1421
if (!o.is_boolean)
1422
{
1423
if (o.has_implicit)
1424
{
1425
result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]";
1426
}
1427
else
1428
{
1429
result += " " + arg;
1430
}
1431
}
1432
1433
return result;
1434
}
1435
1436
String
1437
format_description
1438
(
1439
const HelpOptionDetails& o,
1440
size_t start,
1441
size_t width
1442
)
1443
{
1444
auto desc = o.desc;
1445
1446
if (o.has_default && (!o.is_boolean || o.default_value != "false"))
1447
{
1448
desc += toLocalString(" (default: " + o.default_value + ")");
1449
}
1450
1451
String result;
1452
1453
auto current = std::begin(desc);
1454
auto startLine = current;
1455
auto lastSpace = current;
1456
1457
auto size = size_t{};
1458
1459
while (current != std::end(desc))
1460
{
1461
if (*current == ' ')
1462
{
1463
lastSpace = current;
1464
}
1465
1466
if (*current == '\n')
1467
{
1468
startLine = current + 1;
1469
lastSpace = startLine;
1470
}
1471
else if (size > width)
1472
{
1473
if (lastSpace == startLine)
1474
{
1475
stringAppend(result, startLine, current + 1);
1476
stringAppend(result, "\n");
1477
stringAppend(result, start, ' ');
1478
startLine = current + 1;
1479
lastSpace = startLine;
1480
}
1481
else
1482
{
1483
stringAppend(result, startLine, lastSpace);
1484
stringAppend(result, "\n");
1485
stringAppend(result, start, ' ');
1486
startLine = lastSpace + 1;
1487
lastSpace = startLine;
1488
}
1489
size = 0;
1490
}
1491
else
1492
{
1493
++size;
1494
}
1495
1496
++current;
1497
}
1498
1499
//append whatever is left
1500
stringAppend(result, startLine, current);
1501
1502
return result;
1503
}
1504
}
1505
1506
inline
1507
ParseResult::ParseResult
1508
(
1509
const std::shared_ptr<
1510
std::unordered_map<std::string, std::shared_ptr<OptionDetails>>
1511
> options,
1512
std::vector<std::string> positional,
1513
bool allow_unrecognised,
1514
int& argc, char**& argv
1515
)
1516
: m_options(options)
1517
, m_positional(std::move(positional))
1518
, m_next_positional(m_positional.begin())
1519
, m_allow_unrecognised(allow_unrecognised)
1520
{
1521
parse(argc, argv);
1522
}
1523
1524
inline
1525
OptionAdder
1526
Options::add_options(std::string group)
1527
{
1528
return OptionAdder(*this, std::move(group));
1529
}
1530
1531
inline
1532
OptionAdder&
1533
OptionAdder::operator()
1534
(
1535
const std::string& opts,
1536
const std::string& desc,
1537
std::shared_ptr<const Value> value,
1538
std::string arg_help
1539
)
1540
{
1541
std::match_results<const char*> result;
1542
std::regex_match(opts.c_str(), result, option_specifier);
1543
1544
if (result.empty())
1545
{
1546
throw invalid_option_format_error(opts);
1547
}
1548
1549
const auto& short_match = result[2];
1550
const auto& long_match = result[3];
1551
1552
if (!short_match.length() && !long_match.length())
1553
{
1554
throw invalid_option_format_error(opts);
1555
} else if (long_match.length() == 1 && short_match.length())
1556
{
1557
throw invalid_option_format_error(opts);
1558
}
1559
1560
auto option_names = []
1561
(
1562
const std::sub_match<const char*>& short_,
1563
const std::sub_match<const char*>& long_
1564
)
1565
{
1566
if (long_.length() == 1)
1567
{
1568
return std::make_tuple(long_.str(), short_.str());
1569
}
1570
else
1571
{
1572
return std::make_tuple(short_.str(), long_.str());
1573
}
1574
}(short_match, long_match);
1575
1576
m_options.add_option
1577
(
1578
m_group,
1579
std::get<0>(option_names),
1580
std::get<1>(option_names),
1581
desc,
1582
value,
1583
std::move(arg_help)
1584
);
1585
1586
return *this;
1587
}
1588
1589
inline
1590
void
1591
ParseResult::parse_default(std::shared_ptr<OptionDetails> details)
1592
{
1593
m_results[details].parse_default(details);
1594
}
1595
1596
inline
1597
void
1598
ParseResult::parse_option
1599
(
1600
std::shared_ptr<OptionDetails> value,
1601
const std::string& /*name*/,
1602
const std::string& arg
1603
)
1604
{
1605
auto& result = m_results[value];
1606
result.parse(value, arg);
1607
1608
m_sequential.emplace_back(value->long_name(), arg);
1609
}
1610
1611
inline
1612
void
1613
ParseResult::checked_parse_arg
1614
(
1615
int argc,
1616
char* argv[],
1617
int& current,
1618
std::shared_ptr<OptionDetails> value,
1619
const std::string& name
1620
)
1621
{
1622
if (current + 1 >= argc)
1623
{
1624
if (value->value().has_implicit())
1625
{
1626
parse_option(value, name, value->value().get_implicit_value());
1627
}
1628
else
1629
{
1630
throw missing_argument_exception(name);
1631
}
1632
}
1633
else
1634
{
1635
if (value->value().has_implicit())
1636
{
1637
parse_option(value, name, value->value().get_implicit_value());
1638
}
1639
else
1640
{
1641
parse_option(value, name, argv[current + 1]);
1642
++current;
1643
}
1644
}
1645
}
1646
1647
inline
1648
void
1649
ParseResult::add_to_option(const std::string& option, const std::string& arg)
1650
{
1651
auto iter = m_options->find(option);
1652
1653
if (iter == m_options->end())
1654
{
1655
throw option_not_exists_exception(option);
1656
}
1657
1658
parse_option(iter->second, option, arg);
1659
}
1660
1661
inline
1662
bool
1663
ParseResult::consume_positional(std::string a)
1664
{
1665
while (m_next_positional != m_positional.end())
1666
{
1667
auto iter = m_options->find(*m_next_positional);
1668
if (iter != m_options->end())
1669
{
1670
auto& result = m_results[iter->second];
1671
if (!iter->second->value().is_container())
1672
{
1673
if (result.count() == 0)
1674
{
1675
add_to_option(*m_next_positional, a);
1676
++m_next_positional;
1677
return true;
1678
}
1679
else
1680
{
1681
++m_next_positional;
1682
continue;
1683
}
1684
}
1685
else
1686
{
1687
add_to_option(*m_next_positional, a);
1688
return true;
1689
}
1690
}
1691
else
1692
{
1693
throw option_not_exists_exception(*m_next_positional);
1694
}
1695
}
1696
1697
return false;
1698
}
1699
1700
inline
1701
void
1702
Options::parse_positional(std::string option)
1703
{
1704
parse_positional(std::vector<std::string>{std::move(option)});
1705
}
1706
1707
inline
1708
void
1709
Options::parse_positional(std::vector<std::string> options)
1710
{
1711
m_positional = std::move(options);
1712
m_next_positional = m_positional.begin();
1713
1714
m_positional_set.insert(m_positional.begin(), m_positional.end());
1715
}
1716
1717
inline
1718
void
1719
Options::parse_positional(std::initializer_list<std::string> options)
1720
{
1721
parse_positional(std::vector<std::string>(std::move(options)));
1722
}
1723
1724
inline
1725
ParseResult
1726
Options::parse(int& argc, char**& argv)
1727
{
1728
ParseResult result(m_options, m_positional, m_allow_unrecognised, argc, argv);
1729
return result;
1730
}
1731
1732
inline
1733
void
1734
ParseResult::parse(int& argc, char**& argv)
1735
{
1736
int current = 1;
1737
1738
int nextKeep = 1;
1739
1740
bool consume_remaining = false;
1741
1742
while (current != argc)
1743
{
1744
if (strcmp(argv[current], "--") == 0)
1745
{
1746
consume_remaining = true;
1747
++current;
1748
break;
1749
}
1750
1751
std::match_results<const char*> result;
1752
std::regex_match(argv[current], result, option_matcher);
1753
1754
if (result.empty())
1755
{
1756
//not a flag
1757
1758
// but if it starts with a `-`, then it's an error
1759
if (argv[current][0] == '-' && argv[current][1] != '\0') {
1760
if (!m_allow_unrecognised) {
1761
throw option_syntax_exception(argv[current]);
1762
}
1763
}
1764
1765
//if true is returned here then it was consumed, otherwise it is
1766
//ignored
1767
if (consume_positional(argv[current]))
1768
{
1769
}
1770
else
1771
{
1772
argv[nextKeep] = argv[current];
1773
++nextKeep;
1774
}
1775
//if we return from here then it was parsed successfully, so continue
1776
}
1777
else
1778
{
1779
//short or long option?
1780
if (result[4].length() != 0)
1781
{
1782
const std::string& s = result[4];
1783
1784
for (std::size_t i = 0; i != s.size(); ++i)
1785
{
1786
std::string name(1, s[i]);
1787
auto iter = m_options->find(name);
1788
1789
if (iter == m_options->end())
1790
{
1791
if (m_allow_unrecognised)
1792
{
1793
continue;
1794
}
1795
else
1796
{
1797
//error
1798
throw option_not_exists_exception(name);
1799
}
1800
}
1801
1802
auto value = iter->second;
1803
1804
if (i + 1 == s.size())
1805
{
1806
//it must be the last argument
1807
checked_parse_arg(argc, argv, current, value, name);
1808
}
1809
else if (value->value().has_implicit())
1810
{
1811
parse_option(value, name, value->value().get_implicit_value());
1812
}
1813
else
1814
{
1815
//error
1816
throw option_requires_argument_exception(name);
1817
}
1818
}
1819
}
1820
else if (result[1].length() != 0)
1821
{
1822
const std::string& name = result[1];
1823
1824
auto iter = m_options->find(name);
1825
1826
if (iter == m_options->end())
1827
{
1828
if (m_allow_unrecognised)
1829
{
1830
// keep unrecognised options in argument list, skip to next argument
1831
argv[nextKeep] = argv[current];
1832
++nextKeep;
1833
++current;
1834
continue;
1835
}
1836
else
1837
{
1838
//error
1839
throw option_not_exists_exception(name);
1840
}
1841
}
1842
1843
auto opt = iter->second;
1844
1845
//equals provided for long option?
1846
if (result[2].length() != 0)
1847
{
1848
//parse the option given
1849
1850
parse_option(opt, name, result[3]);
1851
}
1852
else
1853
{
1854
//parse the next argument
1855
checked_parse_arg(argc, argv, current, opt, name);
1856
}
1857
}
1858
1859
}
1860
1861
++current;
1862
}
1863
1864
for (auto& opt : *m_options)
1865
{
1866
auto& detail = opt.second;
1867
auto& value = detail->value();
1868
1869
auto& store = m_results[detail];
1870
1871
if(value.has_default() && !store.count() && !store.has_default()){
1872
parse_default(detail);
1873
}
1874
}
1875
1876
if (consume_remaining)
1877
{
1878
while (current < argc)
1879
{
1880
if (!consume_positional(argv[current])) {
1881
break;
1882
}
1883
++current;
1884
}
1885
1886
//adjust argv for any that couldn't be swallowed
1887
while (current != argc) {
1888
argv[nextKeep] = argv[current];
1889
++nextKeep;
1890
++current;
1891
}
1892
}
1893
1894
argc = nextKeep;
1895
1896
}
1897
1898
inline
1899
void
1900
Options::add_option
1901
(
1902
const std::string& group,
1903
const std::string& s,
1904
const std::string& l,
1905
std::string desc,
1906
std::shared_ptr<const Value> value,
1907
std::string arg_help
1908
)
1909
{
1910
auto stringDesc = toLocalString(std::move(desc));
1911
auto option = std::make_shared<OptionDetails>(s, l, stringDesc, value);
1912
1913
if (s.size() > 0)
1914
{
1915
add_one_option(s, option);
1916
}
1917
1918
if (l.size() > 0)
1919
{
1920
add_one_option(l, option);
1921
}
1922
1923
//add the help details
1924
auto& options = m_help[group];
1925
1926
options.options.emplace_back(HelpOptionDetails{s, l, stringDesc,
1927
value->has_default(), value->get_default_value(),
1928
value->has_implicit(), value->get_implicit_value(),
1929
std::move(arg_help),
1930
value->is_container(),
1931
value->is_boolean()});
1932
}
1933
1934
inline
1935
void
1936
Options::add_one_option
1937
(
1938
const std::string& option,
1939
std::shared_ptr<OptionDetails> details
1940
)
1941
{
1942
auto in = m_options->emplace(option, details);
1943
1944
if (!in.second)
1945
{
1946
throw option_exists_error(option);
1947
}
1948
}
1949
1950
inline
1951
String
1952
Options::help_one_group(const std::string& g) const
1953
{
1954
typedef std::vector<std::pair<String, String>> OptionHelp;
1955
1956
auto group = m_help.find(g);
1957
if (group == m_help.end())
1958
{
1959
return "";
1960
}
1961
1962
OptionHelp format;
1963
1964
size_t longest = 0;
1965
1966
String result;
1967
1968
if (!g.empty())
1969
{
1970
result += toLocalString(" " + g + " options:\n");
1971
}
1972
1973
for (const auto& o : group->second.options)
1974
{
1975
if (m_positional_set.find(o.l) != m_positional_set.end() &&
1976
!m_show_positional)
1977
{
1978
continue;
1979
}
1980
1981
auto s = format_option(o);
1982
longest = (std::max)(longest, stringLength(s));
1983
format.push_back(std::make_pair(s, String()));
1984
}
1985
1986
longest = (std::min)(longest, static_cast<size_t>(OPTION_LONGEST));
1987
1988
//widest allowed description
1989
auto allowed = size_t{76} - longest - OPTION_DESC_GAP;
1990
1991
auto fiter = format.begin();
1992
for (const auto& o : group->second.options)
1993
{
1994
if (m_positional_set.find(o.l) != m_positional_set.end() &&
1995
!m_show_positional)
1996
{
1997
continue;
1998
}
1999
2000
auto d = format_description(o, longest + OPTION_DESC_GAP, allowed);
2001
2002
result += fiter->first;
2003
if (stringLength(fiter->first) > longest)
2004
{
2005
result += '\n';
2006
result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' '));
2007
}
2008
else
2009
{
2010
result += toLocalString(std::string(longest + OPTION_DESC_GAP -
2011
stringLength(fiter->first),
2012
' '));
2013
}
2014
result += d;
2015
result += '\n';
2016
2017
++fiter;
2018
}
2019
2020
return result;
2021
}
2022
2023
inline
2024
void
2025
Options::generate_group_help
2026
(
2027
String& result,
2028
const std::vector<std::string>& print_groups
2029
) const
2030
{
2031
for (size_t i = 0; i != print_groups.size(); ++i)
2032
{
2033
const String& group_help_text = help_one_group(print_groups[i]);
2034
if (empty(group_help_text))
2035
{
2036
continue;
2037
}
2038
result += group_help_text;
2039
if (i < print_groups.size() - 1)
2040
{
2041
result += '\n';
2042
}
2043
}
2044
}
2045
2046
inline
2047
void
2048
Options::generate_all_groups_help(String& result) const
2049
{
2050
std::vector<std::string> all_groups;
2051
all_groups.reserve(m_help.size());
2052
2053
for (auto& group : m_help)
2054
{
2055
all_groups.push_back(group.first);
2056
}
2057
2058
generate_group_help(result, all_groups);
2059
}
2060
2061
inline
2062
std::string
2063
Options::help(const std::vector<std::string>& help_groups) const
2064
{
2065
String result = m_help_string + "\nUsage:\n " +
2066
toLocalString(m_program) + " " + toLocalString(m_custom_help);
2067
2068
if (m_positional.size() > 0 && m_positional_help.size() > 0) {
2069
result += " " + toLocalString(m_positional_help);
2070
}
2071
2072
result += "\n\n";
2073
2074
if (help_groups.size() == 0)
2075
{
2076
generate_all_groups_help(result);
2077
}
2078
else
2079
{
2080
generate_group_help(result, help_groups);
2081
}
2082
2083
return toUTF8String(result);
2084
}
2085
2086
inline
2087
const std::vector<std::string>
2088
Options::groups() const
2089
{
2090
std::vector<std::string> g;
2091
2092
std::transform(
2093
m_help.begin(),
2094
m_help.end(),
2095
std::back_inserter(g),
2096
[] (const std::map<std::string, HelpGroupDetails>::value_type& pair)
2097
{
2098
return pair.first;
2099
}
2100
);
2101
2102
return g;
2103
}
2104
2105
inline
2106
const HelpGroupDetails&
2107
Options::group_help(const std::string& group) const
2108
{
2109
return m_help.at(group);
2110
}
2111
2112
}
2113
2114
#endif //CXXOPTS_HPP_INCLUDED
2115
2116