Mon Jul 21 23:01:38 2008 Google Inc. <opensource@google.com>
* google-gflags: version 0.9
* Add the ability to validate a command-line flag (csilvers)
* Add completion support for commandline flags in bash (daven)
* Add -W compile flags to Makefile, when using gcc (csilvers)
* Allow helpstring to be NULL (cristianoc)
* Improved documentation of classes in the .cc file (csilvers)
* Fix python bug with AppendFlagValues + shortnames (jjtswan)
* Use bool instead of int for boolean flags in gflags.py (bcmills)
* Simplify the way we declare flags, now more foolproof (csilvers)
* Better error messages when bool flags collide (colohan)
* Only evaluate DEFINE_foo macro args once (csilvers)
git-svn-id: https://gflags.googlecode.com/svn/trunk@23 6586e3c6-dcc4-952a-343f-ff74eb82781d
diff --git a/src/gflags.cc b/src/gflags.cc
index 78aad91..3d4881a 100644
--- a/src/gflags.cc
+++ b/src/gflags.cc
@@ -32,7 +32,61 @@
// Revamped and reorganized by Craig Silverstein
//
// This file contains the implementation of all our command line flags
-// stuff.
+// stuff. Here's how everything fits together
+//
+// * FlagRegistry owns CommandLineFlags owns FlagValue.
+// * FlagSaver holds a FlagRegistry (saves it at construct time,
+// restores it at destroy time).
+// * CommandLineFlagParser lives outside that hierarchy, but works on
+// CommandLineFlags (modifying the FlagValues).
+// * Free functions like SetCommandLineOption() work via one of the
+// above (such as CommandLineFlagParser).
+//
+// In more detail:
+//
+// -- The main classes that hold flag data:
+//
+// FlagValue holds the current value of a flag. It's
+// pseudo-templatized: every operation on a FlagValue is typed. It
+// also deals with storage-lifetime issues (so flag values don't go
+// away in a destructor), which is why we need a whole class to hold a
+// variable's value.
+//
+// CommandLineFlag is all the information about a single command-line
+// flag. It has a FlagValue for the flag's current value, but also
+// the flag's name, type, etc.
+//
+// FlagRegistry is a collection of CommandLineFlags. There's the
+// global registry, which is where flags defined via DEFINE_foo()
+// live. But it's possible to define your own flag, manually, in a
+// different registry you create. (In practice, multiple registries
+// are used only by FlagSaver).
+//
+// A given FlagValue is owned by exactly one CommandLineFlag. A given
+// CommandLineFlag is owned by exactly one FlagRegistry. FlagRegistry
+// has a lock; any operation that writes to a FlagValue or
+// CommandLineFlag owned by that registry must acquire the
+// FlagRegistry lock before doing so.
+//
+// --- Some other classes and free functions:
+//
+// CommandLineFlagInfo is a client-exposed version of CommandLineFlag.
+// Once it's instantiated, it has no dependencies or relationships
+// with any other part of this file.
+//
+// FlagRegisterer is the helper class used by the DEFINE_* macros to
+// allow work to be done at global initialization time.
+//
+// CommandLineFlagParser is the class that reads from the commandline
+// and instantiates flag values based on that. It needs to poke into
+// the innards of the FlagValue->CommandLineFlag->FlagRegistry class
+// hierarchy to do that. It's careful to acquire the FlagRegistry
+// lock before doing any writing or other non-const actions.
+//
+// GetCommandLineOption is just a hook into registry routines to
+// retrieve a flag based on its name. SetCommandLineOption, on the
+// other hand, hooks into CommandLineFlagParser. Other API functions
+// are, similarly, mostly hooks into the functionality described above.
#include "config.h"
#include <stdio.h> // for snprintf
@@ -42,6 +96,7 @@
#include <assert.h>
#include <fnmatch.h>
#include <pthread.h>
+#include <iostream> // for cerr
#include <string>
#include <map>
#include <vector>
@@ -70,6 +125,7 @@
using std::map;
using std::vector;
using std::pair;
+using std::cerr;
// Special flags, type 1: the 'recursive' flags. They set another flag's val.
DEFINE_string(flagfile, "",
@@ -88,25 +144,36 @@
_START_GOOGLE_NAMESPACE_
-// There are also 'reporting' flags, in commandlineflags_reporting.cc.
-
-static const char kError[] = "ERROR: ";
-
// The help message indicating that the commandline flag has been
// 'stripped'. It will not show up when doing "-help" and its
// variants. The flag is stripped if STRIP_FLAG_HELP is set to 1
// before including google/gflags.h.
+// This is used by this file, and also in commandlineflags_reporting.cc
const char kStrippedFlagHelp[] = "\001\002\003\004 (unknown) \004\003\002\001";
+// This is used by the unittest to test error-exit code
+void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h
+
+namespace {
+
+// There are also 'reporting' flags, in commandlineflags_reporting.cc.
+
+static const char kError[] = "ERROR: ";
+
// Indicates that undefined options are to be ignored.
// Enables deferred processing of flags in dynamically loaded libraries.
static bool allow_command_line_reparsing = false;
static bool logging_is_probably_set_up = false;
-// This is used by the unittest to test error-exit code
-void (*commandlineflags_exitfunc)(int) = &exit; // from stdlib.h
+// This is a 'prototype' validate-function. 'Real' validate
+// functions, take a flag-value as an argument: ValidateFn(bool) or
+// ValidateFn(uint64). However, for easier storage, we strip off this
+// argument and then restore it when actually calling the function on
+// a flag value.
+typedef bool (*ValidateFnProto)();
+
// --------------------------------------------------------------------
// FlagValue
@@ -124,9 +191,12 @@
string ToString() const;
private:
- friend class CommandLineFlag;
- friend class FlagSaverImpl; // calls New()
+ friend class CommandLineFlag; // for many things, including Validate()
+ friend class GOOGLE_NAMESPACE::FlagSaverImpl; // calls New()
+ friend class FlagRegistry; // checks value_buffer_ for flags_by_ptr_ map
template <typename T> friend T GetFromEnv(const char*, const char*, T);
+ friend bool TryParseLocked(const class CommandLineFlag*, FlagValue*,
+ const char*, string*); // for New(), CopyFrom()
enum ValueType {FV_BOOL, FV_INT32, FV_INT64, FV_UINT64, FV_DOUBLE, FV_STRING};
@@ -135,8 +205,13 @@
FlagValue* New() const; // creates a new one with default value
void CopyFrom(const FlagValue& x);
+ // Calls the given validate-fn on value_buffer_, and returns
+ // whatever it returns. But first casts validate_fn_proto to a
+ // function that takes our value as an argument (eg void
+ // (*validate_fn)(bool) for a bool flag).
+ bool Validate(const char* flagname, ValidateFnProto validate_fn_proto) const;
+
void* value_buffer_; // points to the buffer holding our data
- bool we_own_buffer_; // true iff we new-ed the buffer
ValueType type_; // how to interpret value_
FlagValue(const FlagValue&); // no copying!
@@ -262,6 +337,33 @@
}
}
+bool FlagValue::Validate(const char* flagname,
+ ValidateFnProto validate_fn_proto) const {
+ switch (type_) {
+ case FV_BOOL:
+ return reinterpret_cast<bool (*)(const char*, bool)>(
+ validate_fn_proto)(flagname, VALUE_AS(bool));
+ case FV_INT32:
+ return reinterpret_cast<bool (*)(const char*, int32)>(
+ validate_fn_proto)(flagname, VALUE_AS(int32));
+ case FV_INT64:
+ return reinterpret_cast<bool (*)(const char*, int64)>(
+ validate_fn_proto)(flagname, VALUE_AS(int64));
+ case FV_UINT64:
+ return reinterpret_cast<bool (*)(const char*, uint64)>(
+ validate_fn_proto)(flagname, VALUE_AS(uint64));
+ case FV_DOUBLE:
+ return reinterpret_cast<bool (*)(const char*, double)>(
+ validate_fn_proto)(flagname, VALUE_AS(double));
+ case FV_STRING:
+ return reinterpret_cast<bool (*)(const char*, const string&)>(
+ validate_fn_proto)(flagname, VALUE_AS(string));
+ default:
+ assert(false); // unknown type
+ return false;
+ }
+}
+
const char* FlagValue::TypeName() const {
switch (type_) {
case FV_BOOL: return "bool";
@@ -318,6 +420,10 @@
// This represents a single flag, including its name, description,
// default value, and current value. Mostly this serves as a
// struct, though it also knows how to register itself.
+// All CommandLineFlags are owned by a (exactly one)
+// FlagRegistry. If you wish to modify fields in this class, you
+// should acquire the FlagRegistry lock for the registry that owns
+// this flag.
// --------------------------------------------------------------------
class CommandLineFlag {
@@ -334,13 +440,19 @@
string current_value() const { return current_->ToString(); }
string default_value() const { return defvalue_->ToString(); }
const char* type_name() const { return defvalue_->TypeName(); }
+ ValidateFnProto validate_function() const { return validate_fn_proto_; }
void FillCommandLineFlagInfo(struct CommandLineFlagInfo* result);
+ // If validate_fn_proto_ is non-NULL, calls it on value, returns result.
+ bool Validate(const FlagValue& value) const;
+ bool ValidateCurrent() const { return Validate(*current_); }
+
private:
- friend class FlagRegistry; // for SetFlagLocked()
- friend class FlagSaverImpl; // for cloning the values
+ friend class FlagRegistry; // for SetFlagLocked() and setting flags_by_ptr_
+ friend class GOOGLE_NAMESPACE::FlagSaverImpl; // for cloning the values
friend bool GetCommandLineOption(const char*, string*, bool*);
+ friend bool AddFlagValidator(const void*, ValidateFnProto); // set validate_fn
// This copies all the non-const members: modified, processed, defvalue, etc.
void CopyFrom(const CommandLineFlag& src);
@@ -353,6 +465,11 @@
bool modified_; // Set after default assignment?
FlagValue* defvalue_; // Default value for flag
FlagValue* current_; // Current value for flag
+ // This is a casted, 'generic' version of validate_fn, which actually
+ // takes a flag-value as an arg (void (*validate_fn)(bool), say).
+ // When we pass this to current_->Validate(), it will cast it back to
+ // the proper type. This may be NULL to mean we have no validate_fn.
+ ValidateFnProto validate_fn_proto_;
CommandLineFlag(const CommandLineFlag&); // no copying!
void operator=(const CommandLineFlag&);
@@ -362,7 +479,7 @@
const char* filename,
FlagValue* current_val, FlagValue* default_val)
: name_(name), help_(help), file_(filename), modified_(false),
- defvalue_(default_val), current_(current_val) {
+ defvalue_(default_val), current_(current_val), validate_fn_proto_(NULL) {
}
CommandLineFlag::~CommandLineFlag() {
@@ -405,6 +522,7 @@
result->filename = CleanFileName();
UpdateModifiedBit();
result->is_default = !modified_;
+ result->has_validator_fn = validate_function() != NULL;
}
void CommandLineFlag::UpdateModifiedBit() {
@@ -420,6 +538,14 @@
modified_ = src.modified_;
current_->CopyFrom(*src.current_);
defvalue_->CopyFrom(*src.defvalue_);
+ validate_fn_proto_ = src.validate_fn_proto_;
+}
+
+bool CommandLineFlag::Validate(const FlagValue& value) const {
+ if (validate_function() == NULL)
+ return true;
+ else
+ return value.Validate(name(), validate_function());
}
@@ -455,12 +581,18 @@
// Returns the flag object for the specified name, or NULL if not found.
CommandLineFlag* FindFlagLocked(const char* name);
+ // Returns the flag object whose current-value is stored at flag_ptr.
+ // That is, for whom current_->value_buffer_ == flag_ptr
+ CommandLineFlag* FindFlagViaPtrLocked(const void* flag_ptr);
+
// A fancier form of FindFlag that works correctly if name is of the
// form flag=value. In that case, we set key to point to flag, and
- // modify v to point to the value, and return the flag with the
- // given name (or NULL if not found).
+ // modify v to point to the value (if present), and return the flag
+ // with the given name. If the flag does not exist, returns NULL
+ // and sets error_message.
CommandLineFlag* SplitArgumentLocked(const char* argument,
- string* key, const char** v);
+ string* key, const char** v,
+ string* error_message);
// Set the value of a flag. If the flag was successfully set to
// value, set msg to indicate the new flag-value, and return true.
@@ -472,14 +604,22 @@
static FlagRegistry* GlobalRegistry(); // returns a singleton registry
private:
- friend class FlagSaverImpl; // reads all the flags in order to copy them
- friend void GetAllFlags(vector<CommandLineFlagInfo>*);
+ friend class GOOGLE_NAMESPACE::FlagSaverImpl; // reads all the flags in order to copy them
+ friend class CommandLineFlagParser; // for ValidateAllFlags
+ friend void GOOGLE_NAMESPACE::GetAllFlags(vector<CommandLineFlagInfo>*);
+ // The map from name to flag, for FindFlagLocked().
typedef map<const char*, CommandLineFlag*, StringCmp> FlagMap;
typedef FlagMap::iterator FlagIterator;
typedef FlagMap::const_iterator FlagConstIterator;
FlagMap flags_;
+
+ // The map from current-value pointer to flag, fo FindFlagViaPtrLocked().
+ typedef map<const void*, CommandLineFlag*> FlagPtrMap;
+ FlagPtrMap flags_by_ptr_;
+
pthread_mutex_t lock_;
+
static FlagRegistry* global_registry_; // a singleton registry
static pthread_once_t global_registry_once_;
static int global_registry_once_nothreads_; // when we don't link pthreads
@@ -491,6 +631,15 @@
FlagRegistry& operator=(const FlagRegistry&);
};
+class FlagRegistryLock {
+ public:
+ explicit FlagRegistryLock(FlagRegistry* fr) : fr_(fr) { fr_->Lock(); }
+ ~FlagRegistryLock() { fr_->Unlock(); }
+ private:
+ FlagRegistry *const fr_;
+};
+
+
void FlagRegistry::RegisterFlag(CommandLineFlag* flag) {
Lock();
pair<FlagIterator, bool> ins =
@@ -513,6 +662,8 @@
}
commandlineflags_exitfunc(1); // almost certainly exit()
}
+ // Also add to the flags_by_ptr_ map.
+ flags_by_ptr_[flag->current_->value_buffer_] = flag;
Unlock();
}
@@ -525,9 +676,19 @@
}
}
+CommandLineFlag* FlagRegistry::FindFlagViaPtrLocked(const void* flag_ptr) {
+ FlagPtrMap::const_iterator i = flags_by_ptr_.find(flag_ptr);
+ if (i == flags_by_ptr_.end()) {
+ return NULL;
+ } else {
+ return i->second;
+ }
+}
+
CommandLineFlag* FlagRegistry::SplitArgumentLocked(const char* arg,
string* key,
- const char** v) {
+ const char** v,
+ string* error_message) {
// Find the flag object for this option
const char* flag_name;
const char* value = strchr(arg, '=');
@@ -542,24 +703,36 @@
flag_name = key->c_str();
CommandLineFlag* flag = FindFlagLocked(flag_name);
- if (flag == NULL && (flag_name[0] == 'n') && (flag_name[1] == 'o')) {
- // See if we can find a boolean flag named "x" for an option
- // named "nox".
- flag = FindFlagLocked(flag_name+2);
- if (flag != NULL) {
- if (strcmp(flag->type_name(), "bool") != 0) {
- // This is not a boolean flag, so we should not strip the "no" prefix
- flag = NULL;
- } else {
- // Make up a fake value to replace the "no" we stripped out
- key->assign(flag_name+2); // the name without the "no"
- *v = "0";
- }
- }
- }
if (flag == NULL) {
- return NULL;
+ // If we can't find the flag-name, then we should return an error.
+ // The one exception is if 1) the flag-name is 'nox', 2) there
+ // exists a flag named 'x', and 3) 'x' is a boolean flag.
+ // In that case, we want to return flag 'x'.
+ if (!(flag_name[0] == 'n' && flag_name[1] == 'o')) {
+ // flag-name is not 'nox', so we're not in the exception case.
+ *error_message = (string(kError) +
+ "unknown command line flag '" + *key + "'\n");
+ return NULL;
+ }
+ flag = FindFlagLocked(flag_name+2);
+ if (flag == NULL) {
+ // No flag named 'x' exists, so we're not in the exception case.
+ *error_message = (string(kError) +
+ "unknown command line flag '" + *key + "'\n");
+ return NULL;
+ }
+ if (strcmp(flag->type_name(), "bool") != 0) {
+ // 'x' exists but is not boolean, so we're not in the exception case.
+ *error_message = (string(kError) +
+ "boolean value (" + *key + ") specified for " +
+ flag->type_name() + " command line flag\n");
+ return NULL;
+ }
+ // We're in the exception case!
+ // Make up a fake value to replace the "no" we stripped out
+ key->assign(flag_name+2); // the name without the "no"
+ *v = "0";
}
// Assign a value if this is a boolean flag
@@ -570,20 +743,34 @@
return flag;
}
-// Can't make this static because of friendship.
-inline bool TryParse(const CommandLineFlag* flag, FlagValue* flag_value,
- const char* value, string* msg) {
- if (flag_value->ParseFrom(value)) {
- if (msg)
- *msg += (string(flag->name()) + " set to " + flag_value->ToString()
- + "\n");
- return true;
- } else {
- if (msg)
+bool TryParseLocked(const CommandLineFlag* flag, FlagValue* flag_value,
+ const char* value, string* msg) {
+ // Use tenative_value, not flag_value, until we know value is valid.
+ FlagValue* tentative_value = flag_value->New();
+ if (!tentative_value->ParseFrom(value)) {
+ if (msg) {
*msg += (string(kError) + "illegal value '" + value +
+ "' specified for " + flag->type_name() + " flag '"
+ flag->name() + "'\n");
+ }
+ delete tentative_value;
return false;
+ } else if (!flag->Validate(*tentative_value)) {
+ if (msg){
+ *msg += (string(kError) + "failed validation of new value "
+ + "'" + tentative_value->ToString() + "' for flag '" +
+ + flag->name() + "'\n");
+ }
+ delete tentative_value;
+ return false;
+ } else {
+ flag_value->CopyFrom(*tentative_value);
+ if (msg) {
+ *msg += (string(flag->name()) + " set to " + flag_value->ToString()
+ + "\n");
+ }
+ delete tentative_value;
+ return true;
}
}
@@ -595,7 +782,7 @@
switch (set_mode) {
case SET_FLAGS_VALUE: {
// set or modify the flag's value
- if (!TryParse(flag, flag->current_, value, msg))
+ if (!TryParseLocked(flag, flag->current_, value, msg))
return false;
flag->modified_ = true;
break;
@@ -603,7 +790,7 @@
case SET_FLAG_IF_DEFAULT: {
// set the flag's value, but only if it hasn't been set by someone else
if (!flag->modified_) {
- if (!TryParse(flag, flag->current_, value, msg))
+ if (!TryParseLocked(flag, flag->current_, value, msg))
return false;
flag->modified_ = true;
} else {
@@ -613,11 +800,11 @@
}
case SET_FLAGS_DEFAULT: {
// modify the flag's default-value
- if (!TryParse(flag, flag->defvalue_, value, msg))
+ if (!TryParseLocked(flag, flag->defvalue_, value, msg))
return false;
if (!flag->modified_) {
// Need to set both defvalue *and* current, in this case
- TryParse(flag, flag->current_, value, NULL);
+ TryParseLocked(flag, flag->current_, value, NULL);
}
break;
}
@@ -664,148 +851,6 @@
return global_registry_;
}
-
-void FlagsTypeWarn(const char *name) {
- fprintf(stderr, "ERROR: Flag %s is of type bool, "
- "but its default value is not a boolean.\n", name);
- // This can (and one day should) become a compilations error
- //commandlineflags_exitfunc(1); // almost certainly exit()
-}
-
-// --------------------------------------------------------------------
-// FlagRegisterer
-// This class exists merely to have a global constructor (the
-// kind that runs before main(), that goes an initializes each
-// flag that's been declared. Note that it's very important we
-// don't have a destructor that deletes flag_, because that would
-// cause us to delete current_storage/defvalue_storage as well,
-// which can cause a crash if anything tries to access the flag
-// values in a global destructor.
-// --------------------------------------------------------------------
-
-FlagRegisterer::FlagRegisterer(const char* name, const char* type,
- const char* help, const char* filename,
- void* current_storage, void* defvalue_storage) {
- FlagValue* current = new FlagValue(current_storage, type);
- FlagValue* defvalue = new FlagValue(defvalue_storage, type);
- // Importantly, flag_ will never be deleted, so storage is always good.
- flag_ = new CommandLineFlag(name, help, filename, current, defvalue);
- FlagRegistry::GlobalRegistry()->RegisterFlag(flag_); // default registry
-}
-
-
-// --------------------------------------------------------------------
-// GetAllFlags()
-// The main way the FlagRegistry class exposes its data. This
-// returns, as strings, all the info about all the flags in
-// the main registry, sorted first by filename they are defined
-// in, and then by flagname.
-// --------------------------------------------------------------------
-
-struct FilenameFlagnameCmp {
- bool operator()(const CommandLineFlagInfo& a,
- const CommandLineFlagInfo& b) const {
- int cmp = strcmp(a.filename.c_str(), b.filename.c_str());
- if (cmp == 0)
- cmp = strcmp(a.name.c_str(), b.name.c_str()); // secondary sort key
- return cmp < 0;
- }
-};
-
-void GetAllFlags(vector<CommandLineFlagInfo>* OUTPUT) {
- FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
- registry->Lock();
- for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
- i != registry->flags_.end(); ++i) {
- CommandLineFlagInfo fi;
- i->second->FillCommandLineFlagInfo(&fi);
- OUTPUT->push_back(fi);
- }
- registry->Unlock();
- // Now sort the flags, first by filename they occur in, then alphabetically
- sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp());
-}
-
-// --------------------------------------------------------------------
-// SetArgv()
-// GetArgvs()
-// GetArgv()
-// GetArgv0()
-// ProgramInvocationName()
-// ProgramInvocationShortName()
-// SetUsageMessage()
-// ProgramUsage()
-// Functions to set and get argv. Typically the setter is called
-// by ParseCommandLineFlags. Also can get the ProgramUsage string,
-// set by SetUsageMessage.
-// --------------------------------------------------------------------
-
-// These values are not protected by a Mutex because they are normally
-// set only once during program startup.
-static const char* argv0 = "UNKNOWN"; // just the program name
-static const char* cmdline = ""; // the entire command-line
-static vector<string> argvs;
-static uint32 argv_sum = 0;
-static const char* program_usage = "Warning: SetUsageMessage() never called";
-static bool program_usage_set = false;
-
-void SetArgv(int argc, const char** argv) {
- static bool called_set_argv = false;
- if (called_set_argv) // we already have an argv for you
- return;
-
- called_set_argv = true;
-
- assert(argc > 0); // every program has at least a progname
- argv0 = strdup(argv[0]); // small memory leak, but fn only called once
- assert(argv0);
-
- string cmdline_string = string(""); // easier than doing strcats
- argvs.clear();
- for (int i = 0; i < argc; i++) {
- if (i != 0)
- cmdline_string += " ";
- cmdline_string += argv[i];
- argvs.push_back(argv[i]);
- }
- cmdline = strdup(cmdline_string.c_str()); // another small memory leak
- assert(cmdline);
-
- // Compute a simple sum of all the chars in argv
- argv_sum = 0;
- for (const char* c = cmdline; *c; c++)
- argv_sum += *c;
-}
-
-const vector<string>& GetArgvs() { return argvs; }
-const char* GetArgv() { return cmdline; }
-const char* GetArgv0() { return argv0; }
-uint32 GetArgvSum() { return argv_sum; }
-const char* ProgramInvocationName() { // like the GNU libc fn
- return GetArgv0();
-}
-const char* ProgramInvocationShortName() { // like the GNU libc fn
- const char* slash = strrchr(argv0, '/');
-#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
- if (!slash) slash = strrchr(argv0, '\\');
-#endif
- return slash ? slash + 1 : argv0;
-}
-
-void SetUsageMessage(const string& usage) {
- if (program_usage_set) {
- fprintf(stderr, "ERROR: SetUsageMessage() called more than once\n");
- commandlineflags_exitfunc(1); // almost certainly exit()
- }
-
- program_usage = strdup(usage.c_str()); // small memory leak
- program_usage_set = true;
-}
-
-const char* ProgramUsage() {
- return program_usage;
-}
-
// --------------------------------------------------------------------
// CommandLineFlagParser
// Parsing is done in two stages. In the first, we go through
@@ -815,7 +860,7 @@
// along with an explanation of the trouble. In stage 2, we
// handle the 'reporting' flags like --help and --mpm_version.
// (This is via a call to HandleCommandLineHelpFlags(), in
-// commandlineflags_reporting.cc.)
+// gflags_reporting.cc.)
// An optional stage 3 prints out the error messages.
// This is a bit of a simplification. For instance, --flagfile
// is handled as soon as it's seen in stage 1, not in stage 2.
@@ -835,9 +880,13 @@
uint32 ParseNewCommandLineFlags(int* argc, char*** argv, bool remove_flags);
// Stage 2: print reporting info and exit, if requested.
- // In commandlineflags_reporting.cc:HandleCommandLineHelpFlags().
+ // In gflags_reporting.cc:HandleCommandLineHelpFlags().
- // Stage 3: report any errors and return true if any were found.
+ // Stage 3: validate all the commandline flags that have validators
+ // registered.
+ void ValidateAllFlags();
+
+ // Stage 4: report any errors and return true if any were found.
bool ReportErrors();
// Set a particular command line option. "newval" is a string
@@ -956,11 +1005,12 @@
// Find the flag object for this option
string key;
const char* value;
- CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value);
+ string error_message;
+ CommandLineFlag* flag = registry_->SplitArgumentLocked(arg, &key, &value,
+ &error_message);
if (flag == NULL) {
undefined_names_[key] = ""; // value isn't actually used
- error_flags_[key] = (string(kError) +
- "unknown command line flag '" + key + "'\n");
+ error_flags_[key] = error_message;
continue;
}
@@ -1079,6 +1129,23 @@
return msg;
}
+void CommandLineFlagParser::ValidateAllFlags() {
+ FlagRegistryLock frl(registry_);
+ for (FlagRegistry::FlagConstIterator i = registry_->flags_.begin();
+ i != registry_->flags_.end(); ++i) {
+ if (!i->second->ValidateCurrent()) {
+ // only set a message if one isn't already there. (If there's
+ // an error message, our job is done, even if it's not exactly
+ // the same error.)
+ if (error_flags_[i->second->name()].empty())
+ error_flags_[i->second->name()] = (string(kError) +
+ "--" + i->second->name() +
+ " must be set on the commandline" +
+ " (default value fails validation)");
+ }
+ }
+}
+
bool CommandLineFlagParser::ReportErrors() {
// error_flags_ indicates errors we saw while parsing.
// But we ignore undefined-names if ok'ed by --undef_ok
@@ -1144,8 +1211,10 @@
name_and_val++; // skip second - too
string key;
const char* value;
+ string error_message;
CommandLineFlag* flag = registry_->SplitArgumentLocked(name_and_val,
- &key, &value);
+ &key, &value,
+ &error_message);
// By API, errors parsing flagfile lines are silently ignored.
if (flag == NULL) {
// "WARNING: flagname '" + key + "' not found\n"
@@ -1181,6 +1250,208 @@
return retval;
}
+// --------------------------------------------------------------------
+// GetFromEnv()
+// AddFlagValidator()
+// These are helper functions for routines like BoolFromEnv() and
+// RegisterFlagValidator, defined below. They're defined here so
+// they can live in the unnamed namespace (which makes friendship
+// declarations for these classes possible).
+// --------------------------------------------------------------------
+
+template<typename T>
+T GetFromEnv(const char *varname, const char* type, T dflt) {
+ const char* const valstr = getenv(varname);
+ if (!valstr)
+ return dflt;
+ FlagValue ifv(new T, type);
+ if (!ifv.ParseFrom(valstr)) {
+ fprintf(stderr, "ERROR: error parsing env variable '%s' with value '%s'\n",
+ varname, valstr);
+ commandlineflags_exitfunc(1);
+ }
+ return OTHER_VALUE_AS(ifv, T);
+}
+
+bool AddFlagValidator(const void* flag_ptr, ValidateFnProto validate_fn_proto) {
+ // We want a lock around this routine, in case two threads try to
+ // add a validator (hopefully the same one!) at once. We could use
+ // our own thread, but we need to loook at the registry anyway, so
+ // we just steal that one.
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ FlagRegistryLock frl(registry);
+ // First, find the flag whose current-flag storage is 'flag'.
+ // This is the CommandLineFlag whose current_->value_buffer_ == flag
+ CommandLineFlag* flag = registry->FindFlagViaPtrLocked(flag_ptr);
+ if (!flag) {
+ // WARNING << "Ignoring RegisterValidateFunction() for flag pointer "
+ // << flag_ptr << ": no flag found at that address";
+ return false;
+ } else if (validate_fn_proto == flag->validate_function()) {
+ return true; // ok to register the same function over and over again
+ } else if (validate_fn_proto != NULL && flag->validate_function() != NULL) {
+ // WARNING << "Ignoring RegisterValidateFunction() for flag '"
+ // << flag->name() << "': validate-fn already registered";
+ return false;
+ } else {
+ flag->validate_fn_proto_ = validate_fn_proto;
+ return true;
+ }
+}
+
+} // end unnamed namespaces
+
+
+// Now define the functions that are exported via the .h file
+
+// --------------------------------------------------------------------
+// FlagRegisterer
+// This class exists merely to have a global constructor (the
+// kind that runs before main(), that goes an initializes each
+// flag that's been declared. Note that it's very important we
+// don't have a destructor that deletes flag_, because that would
+// cause us to delete current_storage/defvalue_storage as well,
+// which can cause a crash if anything tries to access the flag
+// values in a global destructor.
+// --------------------------------------------------------------------
+
+// TODO(csilvers): When we're ready to have this error be a fatal one,
+// change this to give a compilation error (via COMPILE_ASSERT(false)).
+bool FlagsTypeWarn(const char *name) {
+ cerr << "Flag " << name << " is of type bool, but its default"
+ << " value is not a boolean. NOTE: This will soon be a"
+ << " compilations error!";
+ return false;
+}
+
+FlagRegisterer::FlagRegisterer(const char* name, const char* type,
+ const char* help, const char* filename,
+ void* current_storage, void* defvalue_storage) {
+ if (help == NULL)
+ help = "";
+ // FlagValue expects the type-name to not include any namespace
+ // components, so we get rid of those, if any.
+ if (strchr(type, ':'))
+ type = strrchr(type, ':') + 1;
+ FlagValue* current = new FlagValue(current_storage, type);
+ FlagValue* defvalue = new FlagValue(defvalue_storage, type);
+ // Importantly, flag_ will never be deleted, so storage is always good.
+ CommandLineFlag* flag = new CommandLineFlag(name, help, filename,
+ current, defvalue);
+ FlagRegistry::GlobalRegistry()->RegisterFlag(flag); // default registry
+}
+
+// --------------------------------------------------------------------
+// GetAllFlags()
+// The main way the FlagRegistry class exposes its data. This
+// returns, as strings, all the info about all the flags in
+// the main registry, sorted first by filename they are defined
+// in, and then by flagname.
+// --------------------------------------------------------------------
+
+struct FilenameFlagnameCmp {
+ bool operator()(const CommandLineFlagInfo& a,
+ const CommandLineFlagInfo& b) const {
+ int cmp = strcmp(a.filename.c_str(), b.filename.c_str());
+ if (cmp == 0)
+ cmp = strcmp(a.name.c_str(), b.name.c_str()); // secondary sort key
+ return cmp < 0;
+ }
+};
+
+void GetAllFlags(vector<CommandLineFlagInfo>* OUTPUT) {
+ FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
+ registry->Lock();
+ for (FlagRegistry::FlagConstIterator i = registry->flags_.begin();
+ i != registry->flags_.end(); ++i) {
+ CommandLineFlagInfo fi;
+ i->second->FillCommandLineFlagInfo(&fi);
+ OUTPUT->push_back(fi);
+ }
+ registry->Unlock();
+ // Now sort the flags, first by filename they occur in, then alphabetically
+ sort(OUTPUT->begin(), OUTPUT->end(), FilenameFlagnameCmp());
+}
+
+// --------------------------------------------------------------------
+// SetArgv()
+// GetArgvs()
+// GetArgv()
+// GetArgv0()
+// ProgramInvocationName()
+// ProgramInvocationShortName()
+// SetUsageMessage()
+// ProgramUsage()
+// Functions to set and get argv. Typically the setter is called
+// by ParseCommandLineFlags. Also can get the ProgramUsage string,
+// set by SetUsageMessage.
+// --------------------------------------------------------------------
+
+// These values are not protected by a Mutex because they are normally
+// set only once during program startup.
+static const char* argv0 = "UNKNOWN"; // just the program name
+static const char* cmdline = ""; // the entire command-line
+static vector<string> argvs;
+static uint32 argv_sum = 0;
+static const char* program_usage = "Warning: SetUsageMessage() never called";
+static bool program_usage_set = false;
+
+void SetArgv(int argc, const char** argv) {
+ static bool called_set_argv = false;
+ if (called_set_argv) // we already have an argv for you
+ return;
+
+ called_set_argv = true;
+
+ assert(argc > 0); // every program has at least a progname
+ argv0 = strdup(argv[0]); // small memory leak, but fn only called once
+ assert(argv0);
+
+ string cmdline_string = string(""); // easier than doing strcats
+ argvs.clear();
+ for (int i = 0; i < argc; i++) {
+ if (i != 0)
+ cmdline_string += " ";
+ cmdline_string += argv[i];
+ argvs.push_back(argv[i]);
+ }
+ cmdline = strdup(cmdline_string.c_str()); // another small memory leak
+ assert(cmdline);
+
+ // Compute a simple sum of all the chars in argv
+ argv_sum = 0;
+ for (const char* c = cmdline; *c; c++)
+ argv_sum += *c;
+}
+
+const vector<string>& GetArgvs() { return argvs; }
+const char* GetArgv() { return cmdline; }
+const char* GetArgv0() { return argv0; }
+uint32 GetArgvSum() { return argv_sum; }
+const char* ProgramInvocationName() { // like the GNU libc fn
+ return GetArgv0();
+}
+const char* ProgramInvocationShortName() { // like the GNU libc fn
+ const char* slash = strrchr(argv0, '/');
+#ifdef OS_WINDOWS
+ if (!slash) slash = strrchr(argv0, '\\');
+#endif
+ return slash ? slash + 1 : argv0;
+}
+
+void SetUsageMessage(const string& usage) {
+ if (program_usage_set) {
+ fprintf(stderr, "ERROR: SetUsageMessage() called more than once\n");
+ exit(1);
+ }
+
+ program_usage = strdup(usage.c_str()); // small memory leak
+ program_usage_set = true;
+}
+
+const char* ProgramUsage() {
+ return program_usage;
+}
// --------------------------------------------------------------------
// GetCommandLineOption()
@@ -1207,14 +1478,12 @@
assert(value);
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
- registry->Lock();
+ FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag == NULL) {
- registry->Unlock();
return false;
} else {
*value = flag->current_value();
- registry->Unlock();
return true;
}
}
@@ -1222,15 +1491,13 @@
bool GetCommandLineFlagInfo(const char* name, CommandLineFlagInfo* OUTPUT) {
if (NULL == name) return false;
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
- registry->Lock();
+ FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag == NULL) {
- registry->Unlock();
return false;
} else {
assert(OUTPUT);
flag->FillCommandLineFlagInfo(OUTPUT);
- registry->Unlock();
return true;
}
}
@@ -1248,7 +1515,7 @@
FlagSettingMode set_mode) {
string result;
FlagRegistry* const registry = FlagRegistry::GlobalRegistry();
- registry->Lock();
+ FlagRegistryLock frl(registry);
CommandLineFlag* flag = registry->FindFlagLocked(name);
if (flag) {
CommandLineFlagParser parser(registry);
@@ -1260,7 +1527,6 @@
// result);
}
}
- registry->Unlock();
// The API of this function is that we return empty string on error
return result;
}
@@ -1269,7 +1535,6 @@
return SetCommandLineOptionWithMode(name, value, SET_FLAGS_VALUE);
}
-
// --------------------------------------------------------------------
// FlagSaver
// FlagSaverImpl
@@ -1296,7 +1561,7 @@
// It's an error to call this more than once.
// Must be called when the registry mutex is not held.
void SaveFromRegistry() {
- main_registry_->Lock();
+ FlagRegistryLock frl(main_registry_);
assert(backup_registry_.empty()); // call only once!
for (FlagRegistry::FlagConstIterator it = main_registry_->flags_.begin();
it != main_registry_->flags_.end();
@@ -1310,7 +1575,6 @@
backup->CopyFrom(*main);
backup_registry_.push_back(backup); // add it to a convenient list
}
- main_registry_->Unlock();
}
// Restores the saved flag states into the flag registry. We
@@ -1318,7 +1582,7 @@
// the SaveFromRegistry; if they were, that's trouble! Must be
// called when the registry mutex is not held.
void RestoreToRegistry() {
- main_registry_->Lock();
+ FlagRegistryLock frl(main_registry_);
vector<CommandLineFlag*>::const_iterator it;
for (it = backup_registry_.begin(); it != backup_registry_.end(); ++it) {
CommandLineFlag* main = main_registry_->FindFlagLocked((*it)->name());
@@ -1326,7 +1590,6 @@
main->CopyFrom(**it);
}
}
- main_registry_->Unlock();
}
private:
@@ -1459,20 +1722,6 @@
// DEFINE_bool(myflag, BoolFromEnv("MYFLAG_DEFAULT", false), "whatever");
// --------------------------------------------------------------------
-template<typename T>
-T GetFromEnv(const char *varname, const char* type, T dflt) {
- const char* const valstr = getenv(varname);
- if (!valstr)
- return dflt;
- FlagValue ifv(new T, type);
- if (!ifv.ParseFrom(valstr)) {
- fprintf(stderr, "ERROR: error parsing env variable '%s' with value '%s'\n",
- varname, valstr);
- commandlineflags_exitfunc(1); // almost certainly exit()
- }
- return OTHER_VALUE_AS(ifv, T);
-}
-
bool BoolFromEnv(const char *v, bool dflt) {
return GetFromEnv(v, "bool", dflt);
}
@@ -1495,12 +1744,50 @@
// --------------------------------------------------------------------
+// RegisterFlagValidator()
+// RegisterFlagValidator() is the function that clients use to
+// 'decorate' a flag with a validation function. Once this is
+// done, every time the flag is set (including when the flag
+// is parsed from argv), the validator-function is called.
+// These functions return true if the validator was added
+// successfully, or false if not: the flag already has a validator,
+// (only one allowed per flag), the 1st arg isn't a flag, etc.
+// This function is not thread-safe.
+// --------------------------------------------------------------------
+
+bool RegisterFlagValidator(const bool* flag,
+ bool (*validate_fn)(const char*, bool)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const int32* flag,
+ bool (*validate_fn)(const char*, int32)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const int64* flag,
+ bool (*validate_fn)(const char*, int64)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const uint64* flag,
+ bool (*validate_fn)(const char*, uint64)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const double* flag,
+ bool (*validate_fn)(const char*, double)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+bool RegisterFlagValidator(const string* flag,
+ bool (*validate_fn)(const char*, const string&)) {
+ return AddFlagValidator(flag, reinterpret_cast<ValidateFnProto>(validate_fn));
+}
+
+
+// --------------------------------------------------------------------
// ParseCommandLineFlags()
// ParseCommandLineNonHelpFlags()
// HandleCommandLineHelpFlags()
// This is the main function called from main(), to actually
// parse the commandline. It modifies argc and argv as described
-// at the top of commandlineflags.h. You can also divide this
+// at the top of gflags.h. You can also divide this
// function into two parts, if you want to do work between
// the parsing of the flags and the printing of any help output.
// --------------------------------------------------------------------
@@ -1529,6 +1816,10 @@
if (do_report)
HandleCommandLineHelpFlags(); // may cause us to exit on --help, etc.
+
+ // See if any of the unset flags fail their validation checks
+ parser.ValidateAllFlags();
+
if (parser.ReportErrors()) // may cause us to exit on illegal flags
commandlineflags_exitfunc(1); // almost certainly exit()
return r;