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