/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SK_COMMAND_LINE_FLAGS_H #define SK_COMMAND_LINE_FLAGS_H #include "SkString.h" #include "SkTArray.h" #include "SkTDArray.h" /** * Including this file (and compiling SkCommandLineFlags.cpp) provides command line * parsing. In order to use it, use the following macros in global * namespace: * * DEFINE_bool(name, defaultValue, helpString); * DEFINE_string(name, defaultValue, helpString); * DEFINE_int32(name, defaultValue, helpString); * DEFINE_double(name, defaultValue, helpString); * * Then, in main, call SkCommandLineFlags::SetUsage() to describe usage and call * SkCommandLineFlags::Parse() to parse the flags. Henceforth, each flag can * be referenced using * * FLAGS_name * * For example, the line * * DEFINE_bool(boolean, false, "The variable boolean does such and such"); * * will create the following variable: * * bool FLAGS_boolean; * * which will initially be set to false, and can be set to true by using the * flag "--boolean" on the commandline. "--noboolean" will set FLAGS_boolean * to false. FLAGS_boolean can also be set using "--boolean=true" or * "--boolean true" (where "true" can be replaced by "false", "TRUE", "FALSE", * "1" or "0"). * * The helpString will be printed if the help flag (-h or -help) is used. * * Similarly, the line * * DEFINE_int32(integer, .., ..); * * will create * * int32_t FLAGS_integer; * * and * * DEFINE_double(real, .., ..); * * will create * * double FLAGS_real; * * These flags can be set by specifying, for example, "--integer 7" and * "--real 3.14" on the command line. * * Unlike the others, the line * * DEFINE_string(args, .., ..); * * creates an array: * * SkCommandLineFlags::StringArray FLAGS_args; * * If the default value is the empty string, FLAGS_args will default to a size * of zero. Otherwise it will default to a size of 1 with the default string * as its value. All strings that follow the flag on the command line (until * a string that begins with '-') will be entries in the array. * * Any flag can be referenced from another file after using the following: * * DECLARE_x(name); * * (where 'x' is the type specified in the DEFINE). * * Inspired by gflags (https://code.google.com/p/gflags/). Is not quite as * robust as gflags, but suits our purposes. For example, allows creating * a flag -h or -help which will never be used, since SkCommandLineFlags handles it. * SkCommandLineFlags will also allow creating --flag and --noflag. Uses the same input * format as gflags and creates similarly named variables (i.e. FLAGS_name). * Strings are handled differently (resulting variable will be an array of * strings) so that a flag can be followed by multiple parameters. */ class SkFlagInfo; class SkCommandLineFlags { public: /** * Call to set the help message to be displayed. Should be called before * Parse. */ static void SetUsage(const char* usage); /** * Call at the beginning of main to parse flags created by DEFINE_x, above. * Must only be called once. */ static void Parse(int argc, char** argv); /** * Custom class for holding the arguments for a string flag. * Publicly only has accessors so the strings cannot be modified. */ class StringArray { public: const char* operator[](int i) const { SkASSERT(i >= 0 && i < fStrings.count()); return fStrings[i].c_str(); } int count() const { return fStrings.count(); } bool isEmpty() const { return this->count() == 0; } /** * Returns true iff string is equal to one of the strings in this array. */ bool contains(const char* string) const { for (int i = 0; i < fStrings.count(); i++) { if (fStrings[i].equals(string)) { return true; } } return false; } void set(int i, const char* str) { fStrings[i].set(str); } private: void reset() { fStrings.reset(); } void append(const char* string) { fStrings.push_back().set(string); } void append(const char* string, size_t length) { fStrings.push_back().set(string, length); } SkTArray fStrings; friend class SkFlagInfo; }; /* Takes a list of the form [~][^]match[$] ~ causes a matching test to always be skipped ^ requires the start of the test to match $ requires the end of the test to match ^ and $ requires an exact match If a test does not match any list entry, it is skipped unless some list entry starts with ~ */ static bool ShouldSkip(const SkTDArray& strings, const char* name); static bool ShouldSkip(const StringArray& strings, const char* name); private: static SkFlagInfo* gHead; static SkString gUsage; // For access to gHead. friend class SkFlagInfo; }; #define TO_STRING2(s) #s #define TO_STRING(s) TO_STRING2(s) #define DEFINE_bool(name, defaultValue, helpString) \ bool FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ nullptr, \ &FLAGS_##name, \ defaultValue, \ helpString) // bool 2 allows specifying a short name. No check is done to ensure that shortName // is actually shorter than name. #define DEFINE_bool2(name, shortName, defaultValue, helpString) \ bool FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ TO_STRING(shortName),\ &FLAGS_##name, \ defaultValue, \ helpString) #define DECLARE_bool(name) extern bool FLAGS_##name; #define DEFINE_string(name, defaultValue, helpString) \ SkCommandLineFlags::StringArray FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ nullptr, \ &FLAGS_##name, \ defaultValue, \ helpString) // string2 allows specifying a short name. There is an assert that shortName // is only 1 character. #define DEFINE_string2(name, shortName, defaultValue, helpString) \ SkCommandLineFlags::StringArray FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ TO_STRING(shortName), \ &FLAGS_##name, \ defaultValue, \ helpString) #define DECLARE_string(name) extern SkCommandLineFlags::StringArray FLAGS_##name; #define DEFINE_int32(name, defaultValue, helpString) \ int32_t FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ &FLAGS_##name, \ defaultValue, \ helpString) #define DEFINE_int32_2(name, shortName, defaultValue, helpString) \ int32_t FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ TO_STRING(shortName), \ &FLAGS_##name, \ defaultValue, \ helpString) #define DECLARE_int32(name) extern int32_t FLAGS_##name; #define DEFINE_double(name, defaultValue, helpString) \ double FLAGS_##name; \ SK_UNUSED static bool unused_##name = SkFlagInfo::CreateDoubleFlag(TO_STRING(name), \ &FLAGS_##name, \ defaultValue, \ helpString) #define DECLARE_double(name) extern double FLAGS_##name; class SkFlagInfo { public: enum FlagTypes { kBool_FlagType, kString_FlagType, kInt_FlagType, kDouble_FlagType, }; /** * Each CreateFlag function creates an SkFlagInfo of the specified type. The SkFlagInfo * object is appended to a list, which is deleted when SkCommandLineFlags::Parse is called. * Therefore, each call should be made before the call to ::Parse. They are not intended * to be called directly. Instead, use the macros described above. * @param name Long version (at least 2 characters) of the name of the flag. This name can * be referenced on the command line as "--name" to set the value of this flag. * @param shortName Short version (one character) of the name of the flag. This name can * be referenced on the command line as "-shortName" to set the value of this flag. * @param p Pointer to a global variable which holds the value set by SkCommandLineFlags. * @param defaultValue The default value of this flag. The variable pointed to by p will * be set to this value initially. This is also displayed as part of the help output. * @param helpString Explanation of what this flag changes in the program. */ static bool CreateBoolFlag(const char* name, const char* shortName, bool* pBool, bool defaultValue, const char* helpString) { SkFlagInfo* info = new SkFlagInfo(name, shortName, kBool_FlagType, helpString); info->fBoolValue = pBool; *info->fBoolValue = info->fDefaultBool = defaultValue; return true; } /** * See comments for CreateBoolFlag. * @param pStrings Unlike the others, this is a pointer to an array of values. * @param defaultValue Thise default will be parsed so that strings separated by spaces * will be added to pStrings. */ static bool CreateStringFlag(const char* name, const char* shortName, SkCommandLineFlags::StringArray* pStrings, const char* defaultValue, const char* helpString); /** * See comments for CreateBoolFlag. */ static bool CreateIntFlag(const char* name, int32_t* pInt, int32_t defaultValue, const char* helpString) { SkFlagInfo* info = new SkFlagInfo(name, nullptr, kInt_FlagType, helpString); info->fIntValue = pInt; *info->fIntValue = info->fDefaultInt = defaultValue; return true; } static bool CreateIntFlag(const char* name, const char* shortName, int32_t* pInt, int32_t defaultValue, const char* helpString) { SkFlagInfo* info = new SkFlagInfo(name, shortName, kInt_FlagType, helpString); info->fIntValue = pInt; *info->fIntValue = info->fDefaultInt = defaultValue; return true; } /** * See comments for CreateBoolFlag. */ static bool CreateDoubleFlag(const char* name, double* pDouble, double defaultValue, const char* helpString) { SkFlagInfo* info = new SkFlagInfo(name, nullptr, kDouble_FlagType, helpString); info->fDoubleValue = pDouble; *info->fDoubleValue = info->fDefaultDouble = defaultValue; return true; } /** * Returns true if the string matches this flag. * For a boolean flag, also sets the value, since a boolean flag can be set in a number of ways * without looking at the following string: * --name * --noname * --name=true * --name=false * --name=1 * --name=0 * --name=TRUE * --name=FALSE */ bool match(const char* string); FlagTypes getFlagType() const { return fFlagType; } void resetStrings() { if (kString_FlagType == fFlagType) { fStrings->reset(); } else { SkDEBUGFAIL("Can only call resetStrings on kString_FlagType"); } } void append(const char* string) { if (kString_FlagType == fFlagType) { fStrings->append(string); } else { SkDEBUGFAIL("Can only append to kString_FlagType"); } } void setInt(int32_t value) { if (kInt_FlagType == fFlagType) { *fIntValue = value; } else { SkDEBUGFAIL("Can only call setInt on kInt_FlagType"); } } void setDouble(double value) { if (kDouble_FlagType == fFlagType) { *fDoubleValue = value; } else { SkDEBUGFAIL("Can only call setDouble on kDouble_FlagType"); } } void setBool(bool value) { if (kBool_FlagType == fFlagType) { *fBoolValue = value; } else { SkDEBUGFAIL("Can only call setBool on kBool_FlagType"); } } SkFlagInfo* next() { return fNext; } const SkString& name() const { return fName; } const SkString& shortName() const { return fShortName; } const SkString& help() const { return fHelpString; } SkString defaultValue() const { SkString result; switch (fFlagType) { case SkFlagInfo::kBool_FlagType: result.printf("%s", fDefaultBool ? "true" : "false"); break; case SkFlagInfo::kString_FlagType: return fDefaultString; case SkFlagInfo::kInt_FlagType: result.printf("%i", fDefaultInt); break; case SkFlagInfo::kDouble_FlagType: result.printf("%2.2f", fDefaultDouble); break; default: SkDEBUGFAIL("Invalid flag type"); } return result; } SkString typeAsString() const { switch (fFlagType) { case SkFlagInfo::kBool_FlagType: return SkString("bool"); case SkFlagInfo::kString_FlagType: return SkString("string"); case SkFlagInfo::kInt_FlagType: return SkString("int"); case SkFlagInfo::kDouble_FlagType: return SkString("double"); default: SkDEBUGFAIL("Invalid flag type"); return SkString(); } } private: SkFlagInfo(const char* name, const char* shortName, FlagTypes type, const char* helpString) : fName(name) , fShortName(shortName) , fFlagType(type) , fHelpString(helpString) , fBoolValue(nullptr) , fDefaultBool(false) , fIntValue(nullptr) , fDefaultInt(0) , fDoubleValue(nullptr) , fDefaultDouble(0) , fStrings(nullptr) { fNext = SkCommandLineFlags::gHead; SkCommandLineFlags::gHead = this; SkASSERT(name && strlen(name) > 1); SkASSERT(nullptr == shortName || 1 == strlen(shortName)); } /** * Set a StringArray to hold the values stored in defaultStrings. * @param array The StringArray to modify. * @param defaultStrings Space separated list of strings that should be inserted into array * individually. */ static void SetDefaultStrings(SkCommandLineFlags::StringArray* array, const char* defaultStrings); // Name of the flag, without initial dashes SkString fName; SkString fShortName; FlagTypes fFlagType; SkString fHelpString; bool* fBoolValue; bool fDefaultBool; int32_t* fIntValue; int32_t fDefaultInt; double* fDoubleValue; double fDefaultDouble; SkCommandLineFlags::StringArray* fStrings; // Both for the help string and in case fStrings is empty. SkString fDefaultString; // In order to keep a linked list. SkFlagInfo* fNext; }; #endif // SK_COMMAND_LINE_FLAGS_H