1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SK_COMMAND_LINE_FLAGS_H 9 #define SK_COMMAND_LINE_FLAGS_H 10 11 #include "../private/SkTArray.h" 12 #include "../private/SkTDArray.h" 13 #include "SkString.h" 14 15 /** 16 * Including this file (and compiling SkCommandLineFlags.cpp) provides command line 17 * parsing. In order to use it, use the following macros in global 18 * namespace: 19 * 20 * DEFINE_bool(name, defaultValue, helpString); 21 * DEFINE_string(name, defaultValue, helpString); 22 * DEFINE_int32(name, defaultValue, helpString); 23 * DEFINE_double(name, defaultValue, helpString); 24 * 25 * Then, in main, call SkCommandLineFlags::SetUsage() to describe usage and call 26 * SkCommandLineFlags::Parse() to parse the flags. Henceforth, each flag can 27 * be referenced using 28 * 29 * FLAGS_name 30 * 31 * For example, the line 32 * 33 * DEFINE_bool(boolean, false, "The variable boolean does such and such"); 34 * 35 * will create the following variable: 36 * 37 * bool FLAGS_boolean; 38 * 39 * which will initially be set to false, and can be set to true by using the 40 * flag "--boolean" on the commandline. "--noboolean" will set FLAGS_boolean 41 * to false. FLAGS_boolean can also be set using "--boolean=true" or 42 * "--boolean true" (where "true" can be replaced by "false", "TRUE", "FALSE", 43 * "1" or "0"). 44 * 45 * The helpString will be printed if the help flag (-h or -help) is used. 46 * 47 * Similarly, the line 48 * 49 * DEFINE_int32(integer, .., ..); 50 * 51 * will create 52 * 53 * int32_t FLAGS_integer; 54 * 55 * and 56 * 57 * DEFINE_double(real, .., ..); 58 * 59 * will create 60 * 61 * double FLAGS_real; 62 * 63 * These flags can be set by specifying, for example, "--integer 7" and 64 * "--real 3.14" on the command line. 65 * 66 * Unlike the others, the line 67 * 68 * DEFINE_string(args, .., ..); 69 * 70 * creates an array: 71 * 72 * SkCommandLineFlags::StringArray FLAGS_args; 73 * 74 * If the default value is the empty string, FLAGS_args will default to a size 75 * of zero. Otherwise it will default to a size of 1 with the default string 76 * as its value. All strings that follow the flag on the command line (until 77 * a string that begins with '-') will be entries in the array. 78 * 79 * DEFINE_extended_string(args, .., .., extendedHelpString); 80 * 81 * creates a similar string array flag as DEFINE_string. The flag will have extended help text 82 * (extendedHelpString) that can the user can see with '--help <args>' flag. 83 * 84 * Any flag can be referenced from another file after using the following: 85 * 86 * DECLARE_x(name); 87 * 88 * (where 'x' is the type specified in the DEFINE). 89 * 90 * Inspired by gflags (https://code.google.com/p/gflags/). Is not quite as 91 * robust as gflags, but suits our purposes. For example, allows creating 92 * a flag -h or -help which will never be used, since SkCommandLineFlags handles it. 93 * SkCommandLineFlags will also allow creating --flag and --noflag. Uses the same input 94 * format as gflags and creates similarly named variables (i.e. FLAGS_name). 95 * Strings are handled differently (resulting variable will be an array of 96 * strings) so that a flag can be followed by multiple parameters. 97 */ 98 99 class SkFlagInfo; 100 101 class SkCommandLineFlags { 102 103 public: 104 /** 105 * Call to set the help message to be displayed. Should be called before 106 * Parse. 107 */ 108 static void SetUsage(const char* usage); 109 110 /** 111 * Call this to display the help message. Should be called after SetUsage. 112 */ 113 static void PrintUsage(); 114 115 /** 116 * Call at the beginning of main to parse flags created by DEFINE_x, above. 117 * Must only be called once. 118 */ 119 static void Parse(int argc, char** argv); 120 121 /** 122 * Custom class for holding the arguments for a string flag. 123 * Publicly only has accessors so the strings cannot be modified. 124 */ 125 class StringArray { 126 public: 127 StringArray() { } 128 explicit StringArray(const SkTArray<SkString>& strings) 129 : fStrings(strings) { 130 } 131 const char* operator[](int i) const { 132 SkASSERT(i >= 0 && i < fStrings.count()); 133 return fStrings[i].c_str(); 134 } 135 136 int count() const { 137 return fStrings.count(); 138 } 139 140 bool isEmpty() const { return this->count() == 0; } 141 142 /** 143 * Returns true iff string is equal to one of the strings in this array. 144 */ 145 bool contains(const char* string) const { 146 for (int i = 0; i < fStrings.count(); i++) { 147 if (fStrings[i].equals(string)) { 148 return true; 149 } 150 } 151 return false; 152 } 153 154 void set(int i, const char* str) { 155 fStrings[i].set(str); 156 } 157 158 private: 159 void reset() { fStrings.reset(); } 160 161 void append(const char* string) { 162 fStrings.push_back().set(string); 163 } 164 165 void append(const char* string, size_t length) { 166 fStrings.push_back().set(string, length); 167 } 168 169 SkTArray<SkString> fStrings; 170 171 friend class SkFlagInfo; 172 }; 173 174 /* Takes a list of the form [~][^]match[$] 175 ~ causes a matching test to always be skipped 176 ^ requires the start of the test to match 177 $ requires the end of the test to match 178 ^ and $ requires an exact match 179 If a test does not match any list entry, it is skipped unless some list entry starts with ~ 180 */ 181 static bool ShouldSkip(const SkTDArray<const char*>& strings, const char* name); 182 static bool ShouldSkip(const StringArray& strings, const char* name); 183 184 private: 185 static SkFlagInfo* gHead; 186 static SkString gUsage; 187 188 // For access to gHead. 189 friend class SkFlagInfo; 190 }; 191 192 #define TO_STRING2(s) #s 193 #define TO_STRING(s) TO_STRING2(s) 194 195 #define DEFINE_bool(name, defaultValue, helpString) \ 196 bool FLAGS_##name; \ 197 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ 198 nullptr, \ 199 &FLAGS_##name, \ 200 defaultValue, \ 201 helpString) 202 203 // bool 2 allows specifying a short name. No check is done to ensure that shortName 204 // is actually shorter than name. 205 #define DEFINE_bool2(name, shortName, defaultValue, helpString) \ 206 bool FLAGS_##name; \ 207 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ 208 TO_STRING(shortName),\ 209 &FLAGS_##name, \ 210 defaultValue, \ 211 helpString) 212 213 #define DECLARE_bool(name) extern bool FLAGS_##name; 214 215 #define DEFINE_string(name, defaultValue, helpString) \ 216 SkCommandLineFlags::StringArray FLAGS_##name; \ 217 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 218 nullptr, \ 219 &FLAGS_##name, \ 220 defaultValue, \ 221 helpString, nullptr) 222 #define DEFINE_extended_string(name, defaultValue, helpString, extendedHelpString) \ 223 SkCommandLineFlags::StringArray FLAGS_##name; \ 224 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 225 nullptr, \ 226 &FLAGS_##name, \ 227 defaultValue, \ 228 helpString, \ 229 extendedHelpString) 230 231 // string2 allows specifying a short name. There is an assert that shortName 232 // is only 1 character. 233 #define DEFINE_string2(name, shortName, defaultValue, helpString) \ 234 SkCommandLineFlags::StringArray FLAGS_##name; \ 235 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 236 TO_STRING(shortName), \ 237 &FLAGS_##name, \ 238 defaultValue, \ 239 helpString, nullptr) 240 241 #define DECLARE_string(name) extern SkCommandLineFlags::StringArray FLAGS_##name; 242 243 244 245 246 #define DEFINE_int32(name, defaultValue, helpString) \ 247 int32_t FLAGS_##name; \ 248 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ 249 &FLAGS_##name, \ 250 defaultValue, \ 251 helpString) 252 253 #define DEFINE_int32_2(name, shortName, defaultValue, helpString) \ 254 int32_t FLAGS_##name; \ 255 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ 256 TO_STRING(shortName), \ 257 &FLAGS_##name, \ 258 defaultValue, \ 259 helpString) 260 261 #define DECLARE_int32(name) extern int32_t FLAGS_##name; 262 263 #define DEFINE_double(name, defaultValue, helpString) \ 264 double FLAGS_##name; \ 265 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateDoubleFlag(TO_STRING(name), \ 266 &FLAGS_##name, \ 267 defaultValue, \ 268 helpString) 269 270 #define DECLARE_double(name) extern double FLAGS_##name; 271 272 class SkFlagInfo { 273 274 public: 275 enum FlagTypes { 276 kBool_FlagType, 277 kString_FlagType, 278 kInt_FlagType, 279 kDouble_FlagType, 280 }; 281 282 /** 283 * Each Create<Type>Flag function creates an SkFlagInfo of the specified type. The SkFlagInfo 284 * object is appended to a list, which is deleted when SkCommandLineFlags::Parse is called. 285 * Therefore, each call should be made before the call to ::Parse. They are not intended 286 * to be called directly. Instead, use the macros described above. 287 * @param name Long version (at least 2 characters) of the name of the flag. This name can 288 * be referenced on the command line as "--name" to set the value of this flag. 289 * @param shortName Short version (one character) of the name of the flag. This name can 290 * be referenced on the command line as "-shortName" to set the value of this flag. 291 * @param p<Type> Pointer to a global variable which holds the value set by SkCommandLineFlags. 292 * @param defaultValue The default value of this flag. The variable pointed to by p<Type> will 293 * be set to this value initially. This is also displayed as part of the help output. 294 * @param helpString Explanation of what this flag changes in the program. 295 */ 296 static bool CreateBoolFlag(const char* name, const char* shortName, bool* pBool, 297 bool defaultValue, const char* helpString) { 298 SkFlagInfo* info = new SkFlagInfo(name, shortName, kBool_FlagType, helpString, nullptr); 299 info->fBoolValue = pBool; 300 *info->fBoolValue = info->fDefaultBool = defaultValue; 301 return true; 302 } 303 304 /** 305 * See comments for CreateBoolFlag. 306 * @param pStrings Unlike the others, this is a pointer to an array of values. 307 * @param defaultValue Thise default will be parsed so that strings separated by spaces 308 * will be added to pStrings. 309 */ 310 static bool CreateStringFlag(const char* name, const char* shortName, 311 SkCommandLineFlags::StringArray* pStrings, 312 const char* defaultValue, const char* helpString, 313 const char* extendedHelpString); 314 315 /** 316 * See comments for CreateBoolFlag. 317 */ 318 static bool CreateIntFlag(const char* name, int32_t* pInt, 319 int32_t defaultValue, const char* helpString) { 320 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kInt_FlagType, helpString, nullptr); 321 info->fIntValue = pInt; 322 *info->fIntValue = info->fDefaultInt = defaultValue; 323 return true; 324 } 325 326 static bool CreateIntFlag(const char* name, const char* shortName, int32_t* pInt, 327 int32_t defaultValue, const char* helpString) { 328 SkFlagInfo* info = new SkFlagInfo(name, shortName, kInt_FlagType, helpString, nullptr); 329 info->fIntValue = pInt; 330 *info->fIntValue = info->fDefaultInt = defaultValue; 331 return true; 332 } 333 334 /** 335 * See comments for CreateBoolFlag. 336 */ 337 static bool CreateDoubleFlag(const char* name, double* pDouble, 338 double defaultValue, const char* helpString) { 339 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kDouble_FlagType, helpString, nullptr); 340 info->fDoubleValue = pDouble; 341 *info->fDoubleValue = info->fDefaultDouble = defaultValue; 342 return true; 343 } 344 345 /** 346 * Returns true if the string matches this flag. 347 * For a boolean flag, also sets the value, since a boolean flag can be set in a number of ways 348 * without looking at the following string: 349 * --name 350 * --noname 351 * --name=true 352 * --name=false 353 * --name=1 354 * --name=0 355 * --name=TRUE 356 * --name=FALSE 357 */ 358 bool match(const char* string); 359 360 FlagTypes getFlagType() const { return fFlagType; } 361 362 void resetStrings() { 363 if (kString_FlagType == fFlagType) { 364 fStrings->reset(); 365 } else { 366 SkDEBUGFAIL("Can only call resetStrings on kString_FlagType"); 367 } 368 } 369 370 void append(const char* string) { 371 if (kString_FlagType == fFlagType) { 372 fStrings->append(string); 373 } else { 374 SkDEBUGFAIL("Can only append to kString_FlagType"); 375 } 376 } 377 378 void setInt(int32_t value) { 379 if (kInt_FlagType == fFlagType) { 380 *fIntValue = value; 381 } else { 382 SkDEBUGFAIL("Can only call setInt on kInt_FlagType"); 383 } 384 } 385 386 void setDouble(double value) { 387 if (kDouble_FlagType == fFlagType) { 388 *fDoubleValue = value; 389 } else { 390 SkDEBUGFAIL("Can only call setDouble on kDouble_FlagType"); 391 } 392 } 393 394 void setBool(bool value) { 395 if (kBool_FlagType == fFlagType) { 396 *fBoolValue = value; 397 } else { 398 SkDEBUGFAIL("Can only call setBool on kBool_FlagType"); 399 } 400 } 401 402 SkFlagInfo* next() { return fNext; } 403 404 const SkString& name() const { return fName; } 405 406 const SkString& shortName() const { return fShortName; } 407 408 const SkString& help() const { return fHelpString; } 409 const SkString& extendedHelp() const { return fExtendedHelpString; } 410 411 SkString defaultValue() const { 412 SkString result; 413 switch (fFlagType) { 414 case SkFlagInfo::kBool_FlagType: 415 result.printf("%s", fDefaultBool ? "true" : "false"); 416 break; 417 case SkFlagInfo::kString_FlagType: 418 return fDefaultString; 419 case SkFlagInfo::kInt_FlagType: 420 result.printf("%i", fDefaultInt); 421 break; 422 case SkFlagInfo::kDouble_FlagType: 423 result.printf("%2.2f", fDefaultDouble); 424 break; 425 default: 426 SkDEBUGFAIL("Invalid flag type"); 427 } 428 return result; 429 } 430 431 SkString typeAsString() const { 432 switch (fFlagType) { 433 case SkFlagInfo::kBool_FlagType: 434 return SkString("bool"); 435 case SkFlagInfo::kString_FlagType: 436 return SkString("string"); 437 case SkFlagInfo::kInt_FlagType: 438 return SkString("int"); 439 case SkFlagInfo::kDouble_FlagType: 440 return SkString("double"); 441 default: 442 SkDEBUGFAIL("Invalid flag type"); 443 return SkString(); 444 } 445 } 446 447 private: 448 SkFlagInfo(const char* name, const char* shortName, FlagTypes type, const char* helpString, 449 const char* extendedHelpString) 450 : fName(name) 451 , fShortName(shortName) 452 , fFlagType(type) 453 , fHelpString(helpString) 454 , fExtendedHelpString(extendedHelpString) 455 , fBoolValue(nullptr) 456 , fDefaultBool(false) 457 , fIntValue(nullptr) 458 , fDefaultInt(0) 459 , fDoubleValue(nullptr) 460 , fDefaultDouble(0) 461 , fStrings(nullptr) { 462 fNext = SkCommandLineFlags::gHead; 463 SkCommandLineFlags::gHead = this; 464 SkASSERT(name && strlen(name) > 1); 465 SkASSERT(nullptr == shortName || 1 == strlen(shortName)); 466 } 467 468 /** 469 * Set a StringArray to hold the values stored in defaultStrings. 470 * @param array The StringArray to modify. 471 * @param defaultStrings Space separated list of strings that should be inserted into array 472 * individually. 473 */ 474 static void SetDefaultStrings(SkCommandLineFlags::StringArray* array, 475 const char* defaultStrings); 476 477 // Name of the flag, without initial dashes 478 SkString fName; 479 SkString fShortName; 480 FlagTypes fFlagType; 481 SkString fHelpString; 482 SkString fExtendedHelpString; 483 bool* fBoolValue; 484 bool fDefaultBool; 485 int32_t* fIntValue; 486 int32_t fDefaultInt; 487 double* fDoubleValue; 488 double fDefaultDouble; 489 SkCommandLineFlags::StringArray* fStrings; 490 // Both for the help string and in case fStrings is empty. 491 SkString fDefaultString; 492 493 // In order to keep a linked list. 494 SkFlagInfo* fNext; 495 }; 496 #endif // SK_COMMAND_LINE_FLAGS_H 497