Line data Source code
1 : // WARNING: Changes to this file must be contributed back to Sawyer or else they will 2 : // be clobbered by the next update from Sawyer. The Sawyer repository is at 3 : // https://github.com/matzke1/sawyer. 4 : 5 : 6 : 7 : 8 : #ifndef Sawyer_CommandLine_Boost_H 9 : #define Sawyer_CommandLine_Boost_H 10 : 11 : #include <Sawyer/CommandLine.h> 12 : #include <Sawyer/Sawyer.h> 13 : 14 : namespace Sawyer { 15 : namespace CommandLine { 16 : 17 : /** Drop-in replacement to help boost users. 18 : * 19 : * Some users use boost::program_options, but due to the complexity of the interface, they use only the most basic 20 : * features. Essentially, they use boost to map switches like "--foo=bar" into a map containing the pair ("foo", "bar") both 21 : * represented as <code>std::string</code>. Their source code then makes decisions and obtains switch values by querying them 22 : * from this map and converting them to a non-string type that's stored in a C++ variable for later reference. 23 : * 24 : * Sawyer::CommandLine can also operate in this "pull" mode although it's most often used in "push" mode, where the 25 : * command-line parsing package is responsible for converting the string argument to a non-string type and storing it in a C++ 26 : * variable somewhere. */ 27 : namespace Boost { 28 : 29 : /** Replacement for basic use of boost::program_options::value. 30 : * 31 : * The equivalent mechanism in Sawyer is split between two class hierarchies: a class that represents parsing of strings into 32 : * non-string types, and a class that represents storing of non-string values into C++ variables. This separation of concerns 33 : * allows Sawyer to parse one type but store it in a variable of a different, compatible type. However, most users of Sawyer 34 : * see these classes as functions like @ref Sawyer::CommandLine::nonNegativeIntegerParser "nonNegativeIntegerParser" taking 35 : * one argument: some integer reference in which to ultimately store the result. */ 36 : template<class T> 37 : struct value {}; 38 : 39 : /** Replacement for basic use of boost::program_options::options_description. 40 : * 41 : * The equivalent mechanism in Sawyer is a @ref Sawyer::CommandLine::SwitchGroup "SwitchGroup" that can hold declarations for 42 : * zero or more switches. */ 43 : struct options_description { 44 : /** The underlying Sawyer mechanism. */ 45 : Sawyer::CommandLine::SwitchGroup sg; 46 : 47 : /** Construct an empty switch group without documentation. */ 48 : options_description() {} 49 : 50 : /** Construct an empty switch group having a title. */ 51 : explicit options_description(const std::string &title): sg(title) {} 52 : 53 : /** Declare a switch of specified type. 54 : * 55 : * As with boost (but not Sawyer) the type of the switch value is wrapped up in a template class called @ref value. 56 : * 57 : * There are often slightly different semantics possible depending on the declaration details, but all that is glossed 58 : * over in this simple wrapper. 59 : * 60 : * @{ */ 61 : options_description& operator()(const std::string &switchName, const value<std::string>&, const std::string &doc); 62 : options_description& operator()(const std::string &switchName, const value<int>&, const std::string &doc); 63 : options_description& operator()(const std::string &switchName, const value<long int>&, const std::string &doc); 64 : options_description& operator()(const std::string &switchName, const value<std::vector<int> >&, const std::string &doc); 65 : options_description& operator()(const std::string &switchName, const value< std::vector<std::string> >&, 66 : const std::string &doc); 67 : options_description& operator()(const std::string &switchName, const std::string &doc); 68 : /** @} */ 69 : 70 : /** Boost intermediate type for adding more switches. 71 : * 72 : * Sawyer does not need an intermediate type for adding switches since it uses an "insert" method and has a dedicated 73 : * @ref Sawyer::CommandLine::Switch "Switch" type to hold all the switch properties. */ 74 : options_description& add_options() { 75 : return *this; 76 : } 77 : 78 : /** Insert other switches into this group. 79 : * 80 : * Copies the @p other switches into this switch group without making any attempt to resolve conflicts when the @p other 81 : * group has switches with the same name as this group. If that happens, you might end up with one switch that parses a 82 : * string and another that parses an integer, and depending on their order, the string declaration might hide the integer 83 : * declarations since strings are a superset of integers. */ 84 : options_description& add(const options_description &other); 85 : 86 : /** Print switch documentation. 87 : * 88 : * Sawyer normally produces manpage-style documentation for an entire program rather than line-oriented documentation for 89 : * just a set of switch declarations. So in order to achieve the desired limited affect, this wrapper instantiates a 90 : * temporary parser that contains these switches and nothing else. */ 91 : void print() const; 92 : }; 93 : 94 : /** Print documentation for a few switches. */ 95 : std::ostream& operator<<(std::ostream &out, const options_description &x); 96 : 97 : /** Wrapper around Sawyer's CommandLine class. */ 98 : struct command_line_parser { 99 : int argc; /**< Argument count saved by c'tor to be available during parsing. */ 100 : char **argv; /**< Arguments saved by c'tor to be available during parsing. */ 101 : Sawyer::CommandLine::Parser parser; /**< Wrapped parser. */ 102 : 103 : /** Construct a parser. 104 : * 105 : * The command-line is not actually parsed here -- its only saved by reference until @ref run is called. */ 106 : command_line_parser(int argc, char *argv[]): argc(argc), argv(argv) { 107 : parser.errorStream(Sawyer::Message::mlog[Sawyer::Message::FATAL]); // use error messages instead of exceptions 108 : } 109 : 110 : /** Insert specified switch declarations. 111 : * 112 : * @{ */ 113 : command_line_parser& options(const options_description&); 114 : command_line_parser& options(const Sawyer::CommandLine::SwitchGroup&); 115 : /** @} */ 116 : 117 : /** Add predefined switches. 118 : * 119 : * The boost version of this method causes boost to capture values of switches that have not been declared. Although 120 : * Sawyer is also capable of skipping over and accumulating arguments that look like switches but which have not been 121 : * declared, doing so is fraught with danger. Imagine the synchronization and missing code problems that would result if 122 : * a source code compiler took this same approach! */ 123 : command_line_parser& allow_unregistered() { return *this; } 124 : 125 : /** Parse command-line. 126 : * 127 : * Parses the command line and return results. If the syntax is good, this also runs the "--help" switch if present. */ 128 : Sawyer::CommandLine::ParserResult run() { 129 : return parser.parse(argc, argv).apply(); 130 : } 131 : }; 132 : 133 : /** Wrapper around parsed values. 134 : * 135 : * Sawyer not only stores the parsed switch value, but also much information about where the value came from, it's original 136 : * string form, etc. */ 137 0 : struct parsed_values { // not a boost::program_options type 138 : Sawyer::CommandLine::ParsedValues pv; /**< Wrapped ParsedValues. */ 139 : 140 : /** Wrap nothing. */ 141 0 : parsed_values() {} 142 : 143 : /** Wrap ParsedValues. */ 144 0 : explicit parsed_values(const Sawyer::CommandLine::ParsedValues &pv): pv(pv) {} 145 : 146 : /** Convert parsed value to another type. 147 : * 148 : * @{ */ 149 : template<class T> 150 : T as() { 151 : return as_helper(T()); 152 : } 153 : 154 : private: 155 : template<class T> 156 : T as_helper(const T&) { 157 : ASSERT_forbid(pv.empty()); 158 : return pv.back().as<T>(); 159 : } 160 : 161 : template<class T> 162 : std::vector<T> as_helper(const std::vector<T>&) { 163 : std::vector<T> retval; 164 : BOOST_FOREACH (const Sawyer::CommandLine::ParsedValue &v, pv) { 165 : Sawyer::CommandLine::ListParser::ValueList elmts = v.as<Sawyer::CommandLine::ListParser::ValueList>(); 166 : BOOST_FOREACH (const Sawyer::CommandLine::ParsedValue &elmt, elmts) 167 : retval.push_back(elmt.as<T>()); 168 : } 169 : return retval; 170 : } 171 : /** @} */ 172 : 173 : }; 174 : 175 : /** Wrapper around ParserResult. */ 176 : struct variables_map { 177 : Sawyer::CommandLine::ParserResult pr; /**< Wrapped ParserResult. */ 178 : 179 : /** Number of times a switch appeared. */ 180 : size_t count(const std::string &swName) const; 181 : 182 : /** Saved values for a switch. */ 183 : parsed_values operator[](const std::string &swName) const; 184 : }; 185 : 186 : /** Transfer parser results to map. */ 187 : void store(const Sawyer::CommandLine::ParserResult &results, variables_map &output); 188 : 189 : /** Transfer map to C++ variables. 190 : * 191 : * This wrapper doesn't try to handle a "push" paradigm, but if you've gone around the wrapper to declare switches using 192 : * Sawyer's interface and those switches were specified with storage locations, the data has already been moved before this 193 : * function is called (it's part of command_line_parser::run). */ 194 : void notify(variables_map&); 195 : 196 : } // namespace 197 : 198 : } // namespace 199 : } // namespace 200 : 201 : #endif