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 #include "mpl_logging.h"
21
22 #include <any>
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;
Initmaplecl::Init84 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;
DisableWithmaplecl::DisableWith93 explicit DisableWith(const std::string &val) : disableWith(val) {}
94 };
95 struct DisableEvery {
96 const std::vector<std::string> &disableEvery;
DisableEverymaplecl::DisableEvery97 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>
operator T()110 operator T()
111 {
112 T *pval = std::any_cast<T>(&val);
113 DEBUG_ASSERT(pval != nullptr, "pval should not be nullptr");
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() const133 bool IsEnabledByUser() const
134 {
135 return isEnabledByUser;
136 }
137
ExpectedVal() const138 ValueExpectedType ExpectedVal() const
139 {
140 return valueExpected;
141 }
142
IsJoinedValPermitted() const143 bool IsJoinedValPermitted() const
144 {
145 return (valueJoined == ValueJoinedType::kValueJoined);
146 }
147
IsVisibleOption() const148 bool IsVisibleOption() const
149 {
150 return (visibleOption == OptionVisibilityType::kVisibleOption);
151 }
152
GetDisabledName() const153 const std::vector<std::string> &GetDisabledName() const
154 {
155 return disableWith;
156 }
157
GetName() const158 virtual std::string GetName() const
159 {
160 DEBUG_ASSERT(names.size() > 0, "names.size should be non zero");
161 return names[0];
162 }
163
GetDescription() const164 const std::string &GetDescription() const
165 {
166 return optDescription;
167 }
168
GetEqualType() const169 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 */
operator T()230 /*implicit*/ operator T()
231 {
232 return GetValue();
233 }
234
235 RetCode Parse(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg) override
236 {
237 RetCode err = RetCode::noError;
238 #ifdef ARK_LITECG_DEBUG
239 auto &key = args[argsIndex];
onstexpr(digitalCheck<T>)240 if constexpr (digitalCheck<T>) {
241 err = ParseDigit(argsIndex, args, keyArg);
242 } else if constexpr (std::is_same_v<std::string, T>) {
243 err = ParseString(argsIndex, args, keyArg);
onstexpr(std::is_same_v<bool, T>)244 } else if constexpr (std::is_same_v<bool, T>) {
245 err = ParseBool(argsIndex, args);
246 } else {
247 /* Type dependent static_assert. Simple static_assert(false") does not work */
248 static_assert(false && sizeof(T), "T not supported");
249 }
250
251 if (err == RetCode::noError) {
252 isEnabledByUser = true;
253 rawKey = key;
254 }
255 #endif
256 return err;
257 }
258
259 void Clear() override
260 {
261 if (defaultValue.isSet) {
262 value = defaultValue.defaultValue;
263 } else {
onstexpr(digitalCheck<T>)264 if constexpr (digitalCheck<T>) {
265 value = 0;
266 } else if constexpr (std::is_same_v<std::string, T>) {
267 value = "";
onstexpr(std::is_same_v<bool, T>)268 } else if constexpr (std::is_same_v<bool, T>) {
269 value = false;
270 } else {
271 /* Type dependent static_assert. Simple static_assert(false") does not work */
272 static_assert(false && sizeof(T), "T not supported");
273 }
274 }
275
276 for (auto &category : optCategories) {
277 category->Remove(this);
278 }
279
280 isEnabledByUser = false;
281 }
282
283 std::vector<std::string> GetRawValues() override
284 {
285 std::vector<std::string> rawVals;
286 FillVal(value, rawVals);
287 return rawVals;
288 }
289
290 std::string GetName() const override
291 {
onstexpr(std::is_same_v<bool, T>)292 if constexpr (std::is_same_v<bool, T>) {
293 DEBUG_ASSERT(names.size() > 0, "names.size should be non zero");
294 return ((value == true) ? names[0] : this->GetDisabledName()[0]);
295 } else {
296 return OptionInterface::GetName();
297 }
298 }
299
GetValue() const300 const T &GetValue() const
301 {
302 return value;
303 }
304
305 OptionWrp GetCommonValue() const override
306 {
307 return value;
308 }
309
SetValue(const T &val)310 virtual void SetValue(const T &val)
311 {
312 value = val;
313 }
314
315 protected:
316 RetCode ParseDigit(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg);
317 RetCode ParseString(size_t &argsIndex, const std::deque<std::string_view> &args, KeyArg &keyArg);
318 RetCode ParseBool(size_t &argsIndex, const std::deque<std::string_view> &args);
319
FillVal(const T &val, std::vector<std::string> &vals)320 void FillVal(const T &val, std::vector<std::string> &vals)
321 {
322 if constexpr (digitalCheck<T>) {
323 vals.emplace_back(std::to_string(val));
324 } else if constexpr (std::is_same_v<std::string, T>) {
325 vals.emplace_back(val);
326 } else if constexpr (std::is_same_v<bool, T>) {
327 vals.emplace_back("");
328 } else {
329 /* Type dependent static_assert. Simple static_assert(false") does not work */
330 static_assert(false && sizeof(T), "T not supported");
331 }
332 }
333
334 struct DefaultValue {
335 T defaultValue;
336 bool isSet = false;
337 } defaultValue;
338
339 private:
340 /* To apply input arguments in any order */
341 template <typename ArgT, typename... ArgsT>
Apply(const ArgT &arg, const ArgsT &... args)342 void Apply(const ArgT &arg, const ArgsT &... args)
343 {
344 if constexpr (std::is_same_v<ValueExpectedType, ArgT>) {
345 SetExpectingAttribute(arg);
346 } else if constexpr (std::is_same_v<ValueJoinedType, ArgT>) {
347 SetJoinAttribute(arg);
348 } else if constexpr (std::is_same_v<OptionVisibilityType, ArgT>) {
349 SetVisibilityAttribute(arg);
350 } else if constexpr (std::is_same_v<DisableWith, ArgT>) {
351 SetDisablingAttribute(arg);
352 } else if constexpr (std::is_same_v<DisableEvery, ArgT>) {
353 SetDisablingAttribute(arg);
354 } else {
355 SetDefaultAttribute(arg);
356 }
357
358 /* It's needed to avoid empty Apply() */
359 if constexpr (sizeof...(ArgsT) > 0) {
360 Apply(args...);
361 }
362 }
363
364 template <typename InitT>
SetDefaultAttribute(const Init<InitT> &initializer)365 void SetDefaultAttribute(const Init<InitT> &initializer)
366 {
367 SetValue(initializer.defaultVal);
368 defaultValue.isSet = true;
369 defaultValue.defaultValue = value;
370 }
371
SetExpectingAttribute(ValueExpectedType value)372 void SetExpectingAttribute(ValueExpectedType value)
373 {
374 valueExpected = value;
375 }
376
SetJoinAttribute(ValueJoinedType value)377 void SetJoinAttribute(ValueJoinedType value)
378 {
379 valueJoined = value;
380 }
381
SetVisibilityAttribute(OptionVisibilityType value)382 void SetVisibilityAttribute(OptionVisibilityType value)
383 {
384 visibleOption = value;
385 }
386
SetDisablingAttribute(const DisableWith &value)387 void SetDisablingAttribute(const DisableWith &value)
388 {
389 disableWith.push_back(value.disableWith);
390 }
391
SetDisablingAttribute(const DisableEvery &value)392 void SetDisablingAttribute(const DisableEvery &value)
393 {
394 for (auto &val : value.disableEvery) {
395 disableWith.push_back(val);
396 }
397 }
398
399 T value;
400 };
401
402 template <typename T>
operator ==(Option<T> &opt, const T &arg)403 bool operator==(Option<T> &opt, const T &arg)
404 {
405 return (opt.GetValue() == arg);
406 }
407
408 template <typename T>
operator ==(const T &arg, Option<T> &opt)409 bool operator==(const T &arg, Option<T> &opt)
410 {
411 return opt == arg;
412 }
413
414 /* To handle the comparation of "const char *" and "Option<string>".
415 * This comparation can not be handled with comparation template above! */
416 template <typename T>
operator ==(Option<T> &opt, const char *arg)417 bool operator==(Option<T> &opt, const char *arg)
418 {
419 return (opt.GetValue() == arg);
420 }
421
422 template <typename T>
operator ==(const char *arg, Option<T> &opt)423 bool operator==(const char *arg, Option<T> &opt)
424 {
425 return opt == arg;
426 }
427
428 template <typename T>
CopyIfEnabled(T &dst, maplecl::Option<T> &src)429 void CopyIfEnabled(T &dst, maplecl::Option<T> &src)
430 {
431 if (src.IsEnabledByUser()) {
432 dst = src;
433 }
434 }
435
436 template <typename T>
CopyIfEnabled(T &dst, const T &src, OptionInterface &opt)437 void CopyIfEnabled(T &dst, const T &src, OptionInterface &opt)
438 {
439 if (opt.IsEnabledByUser()) {
440 dst = src;
441 }
442 }
443
444 template <typename T>
445 class List : public Option<T> {
446 public:
447 // options must not be copyable and assignment
448 List(const List &) = delete;
449 List &operator=(const List &) = delete;
450
451 /* variadic template is used to apply any number of options parameters in any order */
452 template <typename... ArgsT>
List(const std::vector<std::string> &optnames, const std::string &descr, const ArgsT &... args)453 explicit List(const std::vector<std::string> &optnames, const std::string &descr, const ArgsT &... args)
454 : Option<T>(optnames, descr, args...) {};
455
456 template <typename... ArgsT>
List(const std::vector<std::string> &optnames, const std::string &descr, const std::vector<OptionCategoryRefWrp> &optionCategories, const ArgsT &... args)457 explicit List(const std::vector<std::string> &optnames, const std::string &descr,
458 const std::vector<OptionCategoryRefWrp> &optionCategories, const ArgsT &... args)
459 : Option<T>(optnames, descr, optionCategories, args...) {};
460
461 void Clear() override
462 {
463 values.clear();
464 if (this->defaultValue.isSet) {
465 SetValue(this->defaultValue.defaultValue);
466 }
467 this->isEnabledByUser = false;
468
469 for (auto &category : this->optCategories) {
470 category->Remove(this);
471 }
472 }
473
474 void SetValue(const T &val) override
475 {
476 values.push_back(val);
477 }
478
GetValue() const479 const T &GetValue() const
480 {
481 static_assert(false && sizeof(T), "GetValue must be not used for List");
482 return T();
483 }
484
GetValues() const485 const std::vector<T> &GetValues() const
486 {
487 return values;
488 }
489
490 std::vector<std::string> GetRawValues() override
491 {
492 std::vector<std::string> rawVals;
493 for (const auto &val : values) {
494 this->FillVal(val, rawVals);
495 }
496
497 return rawVals;
498 }
499
500 private:
501 std::vector<T> values;
502 };
503
504 } // namespace maplecl
505
506 #endif /* MAPLE_UTIL_INCLUDE_OPTION_H */
507