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