Home | History | Annotate | Download | only in flags
      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