• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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