1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef MAPLE_UTIL_INCLUDE_OPTION_H
17 #define MAPLE_UTIL_INCLUDE_OPTION_H
18
19 #include "cl_parser.h"
20
21 #include <any>
22 #include <cassert>
23 #include <cstdint>
24 #include <deque>
25 #include <functional>
26 #include <map>
27 #include <memory>
28 #include <string>
29 #include <vector>
30 #include <iostream>
31
32 namespace maplecl {
33
34 template <typename T>
35 constexpr inline bool digitalCheck = (std::is_same_v<std::uint8_t, T> || std::is_same_v<std::uint16_t, T> ||
36 std::is_same_v<std::uint32_t, T> || std::is_same_v<std::uint64_t, T> ||
37 std::is_same_v<std::int64_t, T> || std::is_same_v<std::int32_t, T> ||
38 std::is_same_v<std::int16_t, T> || std::is_same_v<std::int8_t, T>);
39
40 /* is key VALUE needed ? */
41 enum class ValueExpectedType {
42 kValueOptional, /* "--key VALUE" and "--key" are allowed */
43 kValueRequired, /* only "--key VALUE" is allowed */
44 kValueDisallowed, /* only "--key" is allowed */
45 };
46
47 /* is "joined option" (like -DMACRO) allowed ? */
48 enum class ValueJoinedType {
49 kValueSeparated,
50 kValueJoined, /* joined option (like -DMACRO) is allowed */
51 };
52
53 /* is option visible from Help ? */
54 enum class OptionVisibilityType {
55 kVisibleOption, /* an Option will be visible in Help */
56 kHidedOption /* an Option will be NOT visible in Help */
57 };
58
59 /* This enum is used as the option attribute to detect "=" in the original option.
60 * It can be useful to forward original option in an external tool.
61 * As example: user sets maple -std=gnu11 ; In this case, maple will forward -std
62 * option in clang, but clang can detect this option only with "=":
63 * clang -std=gnu11 (NOT clang -std gnu11). So this attribute is used for correct forwarding.
64 */
65 enum class EqualType { kWithEqual, kWithoutEqual };
66
67 /* These constexpr are needed to use short name in option description, like this:
68 * maplecl::Option<int32_t> option({"--option"}, "Description", optionalValue);
69 * instead of:
70 * maplecl::Option<int32_t> option({"--option"}, "Description", ValueExpectedType::kValueOptional);
71 */
72 constexpr ValueExpectedType optionalValue = ValueExpectedType::kValueOptional;
73 constexpr ValueExpectedType requiredValue = ValueExpectedType::kValueRequired;
74 constexpr ValueExpectedType disallowedValue = ValueExpectedType::kValueDisallowed;
75 constexpr ValueJoinedType joinedValue = ValueJoinedType::kValueJoined;
76 constexpr ValueJoinedType separatedValue = ValueJoinedType::kValueSeparated;
77 constexpr OptionVisibilityType visible = OptionVisibilityType::kVisibleOption;
78 constexpr OptionVisibilityType hide = OptionVisibilityType::kHidedOption;
79
80 /* Initializer is used to set default value for an option */
81 template <typename T>
82 struct Init {
83 const T &defaultVal;
InitInit84 explicit Init(const T &val) : defaultVal(val) {}
85 };
86
87 /* DisableWithData is used to set additional option name to disable this option.
88 * Example: Base option: --opt; Additional option: --no-opt.
89 * --no-opt will disable this option in this example.
90 */
91 struct DisableWith {
92 const std::string &disableWith;
DisableWithDisableWith93 explicit DisableWith(const std::string &val) : disableWith(val) {}
94 };
95 struct DisableEvery {
96 const std::vector<std::string> &disableEvery;
DisableEveryDisableEvery97 explicit DisableEvery(const std::vector<std::string> &val) : disableEvery(val) {}
98 };
99
100 using OptionCategoryRefWrp = std::reference_wrapper<OptionCategory>;
101
102 class OptionWrp {
103 public:
104 template <typename T>
OptionWrp(T v)105 /* implicit */ OptionWrp(T v) : val(v)
106 {
107 }
108
109 template <typename T>
T()110 operator T()
111 {
112 T *pval = std::any_cast<T>(&val);
113 assert(pval);
114 return *pval;
115 }
116
117 std::any val;
118 };
119
120 /* Interface for templated Option class */
121 class OptionInterface {
122 public:
123 virtual ~OptionInterface() = default;
124
125 virtual RetCode Parse(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg) = 0;
126 virtual void Clear() = 0;
127 virtual std::vector<std::string> GetRawValues() = 0;
128 virtual OptionWrp GetCommonValue() const = 0;
129
130 void FinalizeInitialization(const std::vector<std::string> &optnames, const std::string &descr,
131 const std::vector<OptionCategoryRefWrp> &optionCategories);
132
IsEnabledByUser()133 bool IsEnabledByUser() const
134 {
135 return isEnabledByUser;
136 }
137
ExpectedVal()138 ValueExpectedType ExpectedVal() const
139 {
140 return valueExpected;
141 }
142
IsJoinedValPermitted()143 bool IsJoinedValPermitted() const
144 {
145 return (valueJoined == ValueJoinedType::kValueJoined);
146 }
147
IsVisibleOption()148 bool IsVisibleOption() const
149 {
150 return (visibleOption == OptionVisibilityType::kVisibleOption);
151 }
152
GetDisabledName()153 const std::vector<std::string> &GetDisabledName() const
154 {
155 return disableWith;
156 }
157
GetName()158 virtual std::string GetName() const
159 {
160 assert(names.size() > 0);
161 return names[0];
162 }
163
GetDescription()164 const std::string &GetDescription() const
165 {
166 return optDescription;
167 }
168
GetEqualType()169 EqualType GetEqualType() const
170 {
171 return equalSign;
172 }
173
SetEqualType(EqualType equal)174 void SetEqualType(EqualType equal)
175 {
176 equalSign = equal;
177 }
178
179 std::string rawKey;
180 std::vector<OptionCategory *> optCategories; // The option is registred in these categories
181
182 protected:
183 std::vector<std::string> names; // names of the option
184 std::string optDescription; // overview of option
185 std::vector<std::string> disableWith; // name to disable the option
186
187 bool isEnabledByUser = false; // it's set if the option is in command line
188
189 ValueExpectedType valueExpected = ValueExpectedType::kValueRequired; // whether the value is expected
190 ValueJoinedType valueJoined = ValueJoinedType::kValueSeparated; // Joined option like -DMACRO
191 OptionVisibilityType visibleOption = OptionVisibilityType::kVisibleOption; // Visible in Help
192
193 EqualType equalSign = EqualType::kWithoutEqual;
194 };
195
196 /* Option class describes command line option */
197 template <typename T>
198 class Option : public OptionInterface {
199 public:
200 /* variadic template is used to apply any number of options parameters in any order */
201 template <typename... ArgsT>
Option(const std::vector<std::string> & optnames,const std::string & descr,const ArgsT &...args)202 explicit Option(const std::vector<std::string> &optnames, const std::string &descr, const ArgsT &... args)
203 {
204 /* It's needed to avoid empty Apply() */
205 if constexpr (sizeof...(ArgsT) > 0) {
206 Apply(args...);
207 }
208 FinalizeInitialization(optnames, descr, {});
209 }
210
211 /* variadic template is used to apply any number of options parameters in any order */
212 template <typename... ArgsT>
Option(const std::vector<std::string> & optnames,const std::string & descr,const std::vector<OptionCategoryRefWrp> & optionCategories,const ArgsT &...args)213 explicit Option(const std::vector<std::string> &optnames, const std::string &descr,
214 const std::vector<OptionCategoryRefWrp> &optionCategories, const ArgsT &... args)
215 {
216 /* It's needed to avoid empty Apply() */
217 if constexpr (sizeof...(ArgsT) > 0) {
218 Apply(args...);
219 }
220 FinalizeInitialization(optnames, descr, optionCategories);
221 }
222
223 // options must not be copyable and assignment
224 Option(const Option &) = delete;
225 Option &operator=(const Option &) = delete;
226
227 /* Conversation operator is needed to use the option like this:
228 * strding test = option1; or int dig = option2 - here will be implicit conversation.
229 */
T()230 /*implicit*/ operator T()
231 {
232 return GetValue();
233 }
234
Parse(size_t & argsIndex,const std::deque<std::string_view> & args,KeyArg & keyArg)235 RetCode Parse(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg) override
236 {
237 RetCode err = RetCode::noError;
238 auto &key = args[argsIndex];
239 if constexpr (digitalCheck<T>) {
240 err = ParseDigit(argsIndex, args, keyArg);
241 } else if constexpr (std::is_same_v<std::string, T>) {
242 err = ParseString(argsIndex, args, keyArg);
243 } else if constexpr (std::is_same_v<bool, T>) {
244 err = ParseBool(argsIndex, args);
245 } else {
246 /* Type dependent static_assert. Simple static_assert(false") does not work */
247 static_assert(false && sizeof(T), "T not supported");
248 }
249
250 if (err == RetCode::noError) {
251 isEnabledByUser = true;
252 rawKey = key;
253 }
254 return err;
255 }
256
Clear()257 void Clear() override
258 {
259 if (defaultValue.isSet) {
260 value = defaultValue.defaultValue;
261 } else {
262 if constexpr (digitalCheck<T>) {
263 value = 0;
264 } else if constexpr (std::is_same_v<std::string, T>) {
265 value = "";
266 } else if constexpr (std::is_same_v<bool, T>) {
267 value = false;
268 } else {
269 /* Type dependent static_assert. Simple static_assert(false") does not work */
270 static_assert(false && sizeof(T), "T not supported");
271 }
272 }
273
274 for (auto &category : optCategories) {
275 category->Remove(this);
276 }
277
278 isEnabledByUser = false;
279 }
280
GetRawValues()281 std::vector<std::string> GetRawValues() override
282 {
283 std::vector<std::string> rawVals;
284 FillVal(value, rawVals);
285 return rawVals;
286 }
287
GetName()288 std::string GetName() const override
289 {
290 if constexpr (std::is_same_v<bool, T>) {
291 assert(names.size() > 0);
292 return ((value == true) ? names[0] : this->GetDisabledName()[0]);
293 } else {
294 return OptionInterface::GetName();
295 }
296 }
297
GetValue()298 const T &GetValue() const
299 {
300 return value;
301 }
302
GetCommonValue()303 OptionWrp GetCommonValue() const override
304 {
305 return value;
306 }
307
SetValue(const T & val)308 virtual void SetValue(const T &val)
309 {
310 value = val;
311 }
312
313 protected:
314 RetCode ParseDigit(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg);
315 RetCode ParseString(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg);
316 RetCode ParseBool(size_t &argsIndex, const std::deque<std::string_view> &args);
317
FillVal(const T & val,std::vector<std::string> & vals)318 void FillVal(const T &val, std::vector<std::string> &vals)
319 {
320 if constexpr (digitalCheck<T>) {
321 vals.emplace_back(std::to_string(val));
322 } else if constexpr (std::is_same_v<std::string, T>) {
323 vals.emplace_back(val);
324 } else if constexpr (std::is_same_v<bool, T>) {
325 vals.emplace_back("");
326 } else {
327 /* Type dependent static_assert. Simple static_assert(false") does not work */
328 static_assert(false && sizeof(T), "T not supported");
329 }
330 }
331
332 struct DefaultValue {
333 T defaultValue;
334 bool isSet = false;
335 } defaultValue;
336
337 private:
338 /* To apply input arguments in any order */
339 template <typename ArgT, typename... ArgsT>
Apply(const ArgT & arg,const ArgsT &...args)340 void Apply(const ArgT &arg, const ArgsT &... args)
341 {
342 if constexpr (std::is_same_v<ValueExpectedType, ArgT>) {
343 SetExpectingAttribute(arg);
344 } else if constexpr (std::is_same_v<ValueJoinedType, ArgT>) {
345 SetJoinAttribute(arg);
346 } else if constexpr (std::is_same_v<OptionVisibilityType, ArgT>) {
347 SetVisibilityAttribute(arg);
348 } else if constexpr (std::is_same_v<DisableWith, ArgT>) {
349 SetDisablingAttribute(arg);
350 } else if constexpr (std::is_same_v<DisableEvery, ArgT>) {
351 SetDisablingAttribute(arg);
352 } else {
353 SetDefaultAttribute(arg);
354 }
355
356 /* It's needed to avoid empty Apply() */
357 if constexpr (sizeof...(ArgsT) > 0) {
358 Apply(args...);
359 }
360 }
361
362 template <typename InitT>
SetDefaultAttribute(const Init<InitT> & initializer)363 void SetDefaultAttribute(const Init<InitT> &initializer)
364 {
365 SetValue(initializer.defaultVal);
366 defaultValue.isSet = true;
367 defaultValue.defaultValue = value;
368 }
369
SetExpectingAttribute(ValueExpectedType value)370 void SetExpectingAttribute(ValueExpectedType value)
371 {
372 valueExpected = value;
373 }
374
SetJoinAttribute(ValueJoinedType value)375 void SetJoinAttribute(ValueJoinedType value)
376 {
377 valueJoined = value;
378 }
379
SetVisibilityAttribute(OptionVisibilityType value)380 void SetVisibilityAttribute(OptionVisibilityType value)
381 {
382 visibleOption = value;
383 }
384
SetDisablingAttribute(const DisableWith & value)385 void SetDisablingAttribute(const DisableWith &value)
386 {
387 disableWith.push_back(value.disableWith);
388 }
389
SetDisablingAttribute(const DisableEvery & value)390 void SetDisablingAttribute(const DisableEvery &value)
391 {
392 for (auto &val : value.disableEvery) {
393 disableWith.push_back(val);
394 }
395 }
396
397 T value;
398 };
399
400 template <typename T>
401 bool operator==(Option<T> &opt, const T &arg)
402 {
403 return (opt.GetValue() == arg);
404 }
405
406 template <typename T>
407 bool operator==(const T &arg, Option<T> &opt)
408 {
409 return opt == arg;
410 }
411
412 /* To handle the comparation of "const char *" and "Option<string>".
413 * This comparation can not be handled with comparation template above! */
414 template <typename T>
415 bool operator==(Option<T> &opt, const char *arg)
416 {
417 return (opt.GetValue() == arg);
418 }
419
420 template <typename T>
421 bool operator==(const char *arg, Option<T> &opt)
422 {
423 return opt == arg;
424 }
425
426 template <typename T>
CopyIfEnabled(T & dst,maplecl::Option<T> & src)427 void CopyIfEnabled(T &dst, maplecl::Option<T> &src)
428 {
429 if (src.IsEnabledByUser()) {
430 dst = src;
431 }
432 }
433
434 template <typename T>
CopyIfEnabled(T & dst,const T & src,OptionInterface & opt)435 void CopyIfEnabled(T &dst, const T &src, OptionInterface &opt)
436 {
437 if (opt.IsEnabledByUser()) {
438 dst = src;
439 }
440 }
441
442 template <typename T>
443 class List : public Option<T> {
444 public:
445 // options must not be copyable and assignment
446 List(const List &) = delete;
447 List &operator=(const List &) = delete;
448
449 /* variadic template is used to apply any number of options parameters in any order */
450 template <typename... ArgsT>
List(const std::vector<std::string> & optnames,const std::string & descr,const ArgsT &...args)451 explicit List(const std::vector<std::string> &optnames, const std::string &descr, const ArgsT &... args)
452 : Option<T>(optnames, descr, args...) {};
453
454 template <typename... ArgsT>
List(const std::vector<std::string> & optnames,const std::string & descr,const std::vector<OptionCategoryRefWrp> & optionCategories,const ArgsT &...args)455 explicit List(const std::vector<std::string> &optnames, const std::string &descr,
456 const std::vector<OptionCategoryRefWrp> &optionCategories, const ArgsT &... args)
457 : Option<T>(optnames, descr, optionCategories, args...) {};
458
Clear()459 void Clear() override
460 {
461 values.clear();
462 if (this->defaultValue.isSet) {
463 SetValue(this->defaultValue.defaultValue);
464 }
465 this->isEnabledByUser = false;
466
467 for (auto &category : this->optCategories) {
468 category->Remove(this);
469 }
470 }
471
SetValue(const T & val)472 void SetValue(const T &val) override
473 {
474 values.push_back(val);
475 }
476
GetValue()477 const T &GetValue() const
478 {
479 static_assert(false && sizeof(T), "GetValue must be not used for List");
480 return T();
481 }
482
GetValues()483 const std::vector<T> &GetValues() const
484 {
485 return values;
486 }
487
GetRawValues()488 std::vector<std::string> GetRawValues() override
489 {
490 std::vector<std::string> rawVals;
491 for (const auto &val : values) {
492 this->FillVal(val, rawVals);
493 }
494
495 return rawVals;
496 }
497
498 private:
499 std::vector<T> values;
500 };
501
502 } // namespace maplecl
503
504 #endif /* MAPLE_UTIL_INCLUDE_OPTION_H */
505