#ifndef __FLAGS_FLAGS_HPP__ #define __FLAGS_FLAGS_HPP__ #include // For abort. #include #include #include // For typeid. #include #include #include #include #include "flags/flag.hpp" #include "flags/loader.hpp" #include "flags/parse.hpp" // An abstraction for application/library "flags". An example is // probably best: // ------------------------------------------------------------- // class MyFlags : public virtual FlagsBase // Use 'virtual' for composition! // { // public: // Flags() // { // add(&debug, // "debug", // "Help string for debug", // false); // // add(&name, // "name", // "Help string for name"); // } // bool debug; // Option name; // }; // // ... // // map > values; // values["no-debug"] = None(); // --no-debug // values["debug"] = None(); // --debug // values["debug"] = Option::some("true"); // --debug=true // values["debug"] = Option::some("false"); // --debug=false // values["name"] = Option::some("frank"); // --name=frank // // MyFlags flags; // flags.load(values); // flags.name.isSome() ... // flags.debug ... // ------------------------------------------------------------- // // You can also compose flags provided that each has used "virtual // inheritance": // ------------------------------------------------------------- // Flags flags; // flags.add(...); // Any other flags you want to throw in there. // flags.load(values); // flags.flag_from_myflags1 ... // flags.flag_from_myflags2 ... // ------------------------------------------------------------- // // "Fail early, fail often": // // You can not add duplicate flags, this is checked for you at compile // time for composite flags (e.g., Flag) and also // checked at runtime for any other flags added via inheritance or // Flags::add(...). // // Flags that can not be loaded (e.g., attempting to use the 'no-' // prefix for a flag that is not boolean) will print a message to // standard error and abort the process. // TODO(benh): Provide a boolean which specifies whether or not to // abort on duplicates or load errors. // TODO(benh): Make prefix for environment variables configurable // (e.g., "MESOS_"). namespace flags { class FlagsBase { public: virtual ~FlagsBase() {} virtual void load(const std::map >& values); virtual void load(const std::map& values); typedef std::map::const_iterator const_iterator; const_iterator begin() const { return flags.begin(); } const_iterator end() const { return flags.end(); } template void add(T1* t1, const std::string& name, const std::string& help, const T2& t2); template void add(Option* option, const std::string& name, const std::string& help); protected: template void add(T1 Flags::*t1, const std::string& name, const std::string& help, const T2& t2); template void add(Option Flags::*option, const std::string& name, const std::string& help); void add(const Flag& flag); private: std::map flags; }; // Need to declare/define some explicit subclasses of FlagsBase so // that we can overload the 'Flags::operator FlagsN () const' // functions for each possible type. class _Flags1 : public virtual FlagsBase {}; class _Flags2 : public virtual FlagsBase {}; class _Flags3 : public virtual FlagsBase {}; class _Flags4 : public virtual FlagsBase {}; class _Flags5 : public virtual FlagsBase {}; // TODO(benh): Add some "type constraints" for template paramters to // make sure they are all of type FlagsBase. template class Flags : public virtual Flags1, public virtual Flags2, public virtual Flags3, public virtual Flags4, public virtual Flags5 {}; template void FlagsBase::add( T1* t1, const std::string& name, const std::string& help, const T2& t2) { *t1 = t2; // Set the default. Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T1) == typeid(bool); flag.loader = std::tr1::bind( &Loader::load, t1, std::tr1::function(const std::string&)>( std::tr1::bind(&parse, std::tr1::placeholders::_1)), name, std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*. // Update the help string to include the default value. flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1 ? " (default: " // On same line, add space. : "(default: "; // On newline. flag.help += stringify(t2); flag.help += ")"; FlagsBase::add(flag); } template void FlagsBase::add( Option* option, const std::string& name, const std::string& help) { Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T) == typeid(bool); flag.loader = std::tr1::bind( &OptionLoader::load, option, std::tr1::function(const std::string&)>( std::tr1::bind(&parse, std::tr1::placeholders::_1)), name, std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*. FlagsBase::add(flag); } template void FlagsBase::add( T1 Flags::*t1, const std::string& name, const std::string& help, const T2& t2) { Flags* flags = dynamic_cast(this); if (flags == NULL) { std::cerr << "Attempted to add flag '" << name << "' with incompatible type" << std::endl; abort(); } else { flags->*t1 = t2; // Set the default. } Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T1) == typeid(bool); flag.loader = std::tr1::bind( &MemberLoader::load, std::tr1::placeholders::_1, t1, std::tr1::function(const std::string&)>( std::tr1::bind(&parse, std::tr1::placeholders::_1)), name, std::tr1::placeholders::_2); // Update the help string to include the default value. flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1 ? " (default: " // On same line, add space. : "(default: "; // On newline. flag.help += stringify(t2); flag.help += ")"; flag.help += ")"; add(flag); } template void FlagsBase::add( Option Flags::*option, const std::string& name, const std::string& help) { Flags* flags = dynamic_cast(this); if (flags == NULL) { std::cerr << "Attempted to add flag '" << name << "' with incompatible type" << std::endl; abort(); } Flag flag; flag.name = name; flag.help = help; flag.boolean = typeid(T) == typeid(bool); flag.loader = std::tr1::bind( &OptionMemberLoader::load, std::tr1::placeholders::_1, option, std::tr1::function(const std::string&)>( std::tr1::bind(&parse, std::tr1::placeholders::_1)), name, std::tr1::placeholders::_2); add(flag); } inline void FlagsBase::add(const Flag& flag) { if (flags.count(flag.name) > 0) { std::cerr << "Attempted to add duplicate flag '" << flag.name << "'" << std::endl; abort(); } flags[flag.name] = flag; } inline void FlagsBase::load(const std::map >& values) { std::map >::const_iterator iterator; for (iterator = values.begin(); iterator != values.end(); ++iterator) { const std::string& name = iterator->first; const Option& value = iterator->second; if (flags.count(name) > 0) { if (value.isSome()) { flags[name].loader(this, value.get()); // --name=value } else if (flags[name].boolean) { flags[name].loader(this, "true"); // --name } else { std::cerr << "Failed to load non-boolean flag via '" << name << "'" << std::endl; abort(); } } else if (name.find("no-") == 0 && flags.count(name.substr(3)) > 0) { if (flags[name.substr(3)].boolean) { if (value.isNone()) { flags[name.substr(3)].loader(this, "false"); // --no-name } else { std::cerr << "Failed to load boolean flag '" << name.substr(3) << "' via '" << name << "' with value '" << value.get() << "'" << std::endl; abort(); } } else { std::cerr << "Failed to load non-boolean flag '" << name.substr(3) << "' via '" << name << "'" << std::endl; abort(); } } } } inline void FlagsBase::load(const std::map& _values) { std::map > values; std::map::const_iterator iterator; for (iterator = _values.begin(); iterator != _values.end(); ++iterator) { const std::string& name = iterator->first; const std::string& value = iterator->second; values[name] = Option::some(value); } load(values); } } // namespace flags { #endif // __FLAGS_FLAGS_HPP__