flags.h 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright (c) 2023 Google LLC.
  2. //
  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. #ifndef INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
  15. #define INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_
  16. #include <stdint.h>
  17. #include <functional>
  18. #include <string>
  19. #include <variant>
  20. #include <vector>
  21. // This file provides some utils to define a command-line interface with
  22. // required and optional flags.
  23. // - Flag order is not checked.
  24. // - Currently supported flag types: BOOLEAN, STRING
  25. // - As with most nix tools, using '--' in the command-line means all following
  26. // tokens will be considered positional
  27. // arguments.
  28. // Example: binary -g -- -g --some-other-flag
  29. // - the first `-g` is a flag.
  30. // - the second `-g` is not a flag.
  31. // - `--some-other-flag` is not a flag.
  32. // - Both long-form and short-form flags are supported, but boolean flags don't
  33. // support split boolean literals (short and long form).
  34. // Example:
  35. // -g : allowed, sets g to true.
  36. // --my-flag : allowed, sets --my-flag to true.
  37. // --my-flag=true : allowed, sets --my-flag to true.
  38. // --my-flag true : NOT allowed.
  39. // -g true : NOT allowed.
  40. // --my-flag=TRUE : NOT allowed.
  41. //
  42. // - This implementation also supports string flags:
  43. // -o myfile.spv : allowed, sets -o to `myfile.spv`.
  44. // --output=myfile.spv : allowed, sets --output to `myfile.spv`.
  45. // --output myfile.spv : allowd, sets --output to `myfile.spv`.
  46. //
  47. // Note: then second token is NOT checked for hyphens.
  48. // --output -file.spv
  49. // flag name: `output`
  50. // flag value: `-file.spv`
  51. //
  52. // - This implementation generates flag at compile time. Meaning flag names
  53. // must be valid C++ identifiers.
  54. // However, flags are usually using hyphens for word separation. Hence
  55. // renaming is done behind the scenes. Example:
  56. // // Declaring a long-form flag.
  57. // FLAG_LONG_bool(my_flag, [...])
  58. //
  59. // -> in the code: flags::my_flag.value()
  60. // -> command-line: --my-flag
  61. //
  62. // - The only additional lexing done is around '='. Otherwise token list is
  63. // processed as received in the Parse()
  64. // function.
  65. // Lexing the '=' sign:
  66. // - This is only done when parsing a long-form flag name.
  67. // - the first '=' found is considered a marker for long-form, splitting
  68. // the token into 2.
  69. // Example: --option=value=abc -> [--option, value=abc]
  70. //
  71. // In most cases, you want to define some flags, parse them, and query them.
  72. // Here is a small code sample:
  73. //
  74. // ```c
  75. // // Defines a '-h' boolean flag for help printing, optional.
  76. // FLAG_SHORT_bool(h, /*default=*/ false, "Print the help.", false);
  77. // // Defines a '--my-flag' string flag, required.
  78. // FLAG_LONG_string(my_flag, /*default=*/ "", "A magic flag!", true);
  79. //
  80. // int main(int argc, const char** argv) {
  81. // if (!flags::Parse(argv)) {
  82. // return -1;
  83. // }
  84. //
  85. // if (flags::h.value()) {
  86. // printf("usage: my-bin --my-flag=<value>\n");
  87. // return 0;
  88. // }
  89. //
  90. // printf("flag value: %s\n", flags::my_flag.value().c_str());
  91. // for (const std::string& arg : flags::positional_arguments) {
  92. // printf("arg: %s\n", arg.c_str());
  93. // }
  94. // return 0;
  95. // }
  96. // ```c
  97. // Those macros can be used to define flags.
  98. // - They should be used in the global scope.
  99. // - Underscores in the flag variable name are replaced with hyphens ('-').
  100. //
  101. // Example:
  102. // FLAG_SHORT_bool(my_flag, false, "some help", false);
  103. // - in the code: flags::my_flag
  104. // - command line: --my-flag=true
  105. //
  106. #define FLAG_LONG_string(Name, Default, Required) \
  107. UTIL_FLAGS_FLAG_LONG(std::string, Name, Default, Required)
  108. #define FLAG_LONG_bool(Name, Default, Required) \
  109. UTIL_FLAGS_FLAG_LONG(bool, Name, Default, Required)
  110. #define FLAG_LONG_uint(Name, Default, Required) \
  111. UTIL_FLAGS_FLAG_LONG(uint32_t, Name, Default, Required)
  112. #define FLAG_SHORT_string(Name, Default, Required) \
  113. UTIL_FLAGS_FLAG_SHORT(std::string, Name, Default, Required)
  114. #define FLAG_SHORT_bool(Name, Default, Required) \
  115. UTIL_FLAGS_FLAG_SHORT(bool, Name, Default, Required)
  116. #define FLAG_SHORT_uint(Name, Default, Required) \
  117. UTIL_FLAGS_FLAG_SHORT(uint32_t, Name, Default, Required)
  118. namespace flags {
  119. // Parse the command-line arguments, checking flags, and separating positional
  120. // arguments from flags.
  121. //
  122. // * argv: the argv array received in the main function. This utility expects
  123. // the last pointer to
  124. // be NULL, as it should if coming from the main() function.
  125. //
  126. // Returns `true` if the parsing succeeds, `false` otherwise.
  127. bool Parse(const char** argv);
  128. } // namespace flags
  129. // ===================== BEGIN NON-PUBLIC SECTION =============================
  130. // All the code below belongs to the implementation, and there is no guaranteed
  131. // around the API stability. Please do not use it directly.
  132. // Defines the static variable holding the flag, allowing access like
  133. // flags::my_flag.
  134. // By creating the FlagRegistration object, the flag can be added to
  135. // the global list.
  136. // The final `extern` definition is ONLY useful for clang-format:
  137. // - if the macro doesn't ends with a semicolon, clang-format goes wild.
  138. // - cannot disable clang-format for those macros on clang < 16.
  139. // (https://github.com/llvm/llvm-project/issues/54522)
  140. // - cannot allow trailing semi (-Wextra-semi).
  141. #define UTIL_FLAGS_FLAG(Type, Prefix, Name, Default, Required, IsShort) \
  142. namespace flags { \
  143. Flag<Type> Name(Default); \
  144. namespace { \
  145. static FlagRegistration Name##_registration(Name, Prefix #Name, Required, \
  146. IsShort); \
  147. } \
  148. } \
  149. extern flags::Flag<Type> flags::Name
  150. #define UTIL_FLAGS_FLAG_LONG(Type, Name, Default, Required) \
  151. UTIL_FLAGS_FLAG(Type, "--", Name, Default, Required, false)
  152. #define UTIL_FLAGS_FLAG_SHORT(Type, Name, Default, Required) \
  153. UTIL_FLAGS_FLAG(Type, "-", Name, Default, Required, true)
  154. namespace flags {
  155. // Just a wrapper around the flag value.
  156. template <typename T>
  157. struct Flag {
  158. public:
  159. Flag(T&& default_value) : value_(default_value) {}
  160. Flag(Flag&& other) = delete;
  161. Flag(const Flag& other) = delete;
  162. const T& value() const { return value_; }
  163. T& value() { return value_; }
  164. private:
  165. T value_;
  166. };
  167. // To add support for new flag-types, this needs to be extended, and the visitor
  168. // below.
  169. using FlagType = std::variant<std::reference_wrapper<Flag<std::string>>,
  170. std::reference_wrapper<Flag<bool>>,
  171. std::reference_wrapper<Flag<uint32_t>>>;
  172. template <class>
  173. inline constexpr bool always_false_v = false;
  174. extern std::vector<std::string> positional_arguments;
  175. // Static class keeping track of the flags/arguments values.
  176. class FlagList {
  177. struct FlagInfo {
  178. FlagInfo(FlagType&& flag_, std::string&& name_, bool required_,
  179. bool is_short_)
  180. : flag(std::move(flag_)),
  181. name(std::move(name_)),
  182. required(required_),
  183. is_short(is_short_) {}
  184. FlagType flag;
  185. std::string name;
  186. bool required;
  187. bool is_short;
  188. };
  189. public:
  190. template <typename T>
  191. static void register_flag(Flag<T>& flag, std::string&& name, bool required,
  192. bool is_short) {
  193. get_flags().emplace_back(flag, std::move(name), required, is_short);
  194. }
  195. static bool parse(const char** argv);
  196. #ifdef TESTING
  197. // Flags are supposed to be constant for the whole app execution, hence the
  198. // static storage. Gtest doesn't fork before running a test, meaning we have
  199. // to manually clear the context at teardown.
  200. static void reset() {
  201. get_flags().clear();
  202. positional_arguments.clear();
  203. }
  204. #endif
  205. private:
  206. static std::vector<FlagInfo>& get_flags() {
  207. static std::vector<FlagInfo> flags;
  208. return flags;
  209. }
  210. static bool parse_flag_info(FlagInfo& info, const char*** iterator);
  211. static void print_usage(const char* binary_name,
  212. const std::string& usage_format);
  213. };
  214. template <typename T>
  215. struct FlagRegistration {
  216. FlagRegistration(Flag<T>& flag, std::string&& name, bool required,
  217. bool is_short) {
  218. std::string fixed_name = name;
  219. for (auto& c : fixed_name) {
  220. if (c == '_') {
  221. c = '-';
  222. }
  223. }
  224. FlagList::register_flag(flag, std::move(fixed_name), required, is_short);
  225. }
  226. };
  227. // Explicit deduction guide to avoid `-Wctad-maybe-unsupported`.
  228. template <typename T>
  229. FlagRegistration(Flag<T>&, std::string&&, bool, bool) -> FlagRegistration<T>;
  230. } // namespace flags
  231. #endif // INCLUDE_SPIRV_TOOLS_UTIL_FLAGS_HPP_