1 #ifndef MOAB_PROGRAM_OPTIONS_H
2 #define MOAB_PROGRAM_OPTIONS_H
3 
4 #include <vector>
5 #include <map>
6 #include <string>
7 #include <iostream>
8 
9 
10 class ProgOpt;
11 
12 
13 /** A simple command-line option parser and help utility
14  *
15  * Utility class to specify a program's command-line options arguments, produce a help message
16  * explaining how they work, and parse user's command line input (producing useful errors messages
17  * if any problems arise).  Loosely (okay, very loosely) inspired by boost program_options.
18  *
19  * Options are specified by a comma-separated namestring.  An option named "foo,f" can be specified
20  * three ways on the command line: "-f val", "--foo val", or "--foo=val".  The types of options
21  * and arguments are specified by function templates.  Valid template values for positional argument
22  * and options are int, double, and std::string.  void may also be used in options, and it indicates
23  * a command line option that does not take an argument.
24  *
25  * Example usage:
26  * ProgOptions po( "Example usage of ProgOptions" );
27  * po.addOpt<void>( "verbose,v", "Turn on verbose messages" );
28  * po.addOpt<std::string> ("foo", "Specify the foo string" );
29  * int x = 0;
30  * po.addOpt<int>( ",x", "Specify the x number", &x ); // x will be automatically set when options parsed
31  * po.parseCommandLine( argc, argv );
32  * bool verbose = po.numOptSet("verbose") > 0;
33  * std::string foo;
34  * if( !po.getOpt( "foo", &foo ) )
35  *    foo = "default";
36  * ...
37  *
38  * See the file dagmc_preproc.cpp in the dagmc directory for a real-world example.
39  */
40 class ProgOptions{
41 
42 public:
43 
44   /**
45    * Flags for addOpt and addRequiredArg functions; may be combined with bitwise arithmetic
46    * (though not all combinations make sense!)
47    **/
48 
49 
50   /// Set for a flag that, when detected, prints help text and halts program.
51   /// Constructor creates such a flag by default, so the user shouldn't need to use this directly.
52   static const int help_flag = 1<<0;
53 
54   /// Flag indicating that an option should be given a "cancel" flag.
55   /// This creates, for option --foo, an additional option --no-foo that
56   /// clears all previously read instances of the foo option
57   static const int add_cancel_opt = 1<<1;
58 
59   /// When applied to a flag argument (one with template type void), indicate that the
60   /// value 'false' should be stored into the pointer that was given at option creation time.
61   /// This overrides the default behavior, which is to store the value 'true'.
62   static const int store_false = 1<<2 ;
63 
64   /// Specify a numerical flag where any positive integer is an acceptable
65   /// value.  E.g. --dimension=3 is equivalent to -3.  Only values in the
66   /// range [0,9] are accepted and the flag type must be integer.
67   static const int int_flag = 1<<3 ;
68 
69   /** Substitue any occurance of the '%' symbol in a string with
70    *  the the MPI rank of this process in MPI_COMM_WORLD.  This
71    *  option has no effect if not compiled with MPI.  This flag
72    *  has no effect for non-string options.
73    */
74   static const int rank_subst = 1<<4;
75 
76   /// Set for a flag that, when detected, will call printVersion() and halt the program.
77   static const int version_flag = 1<<5;
78 
79   ///unimplemented flag for required arguments that may be given multiple times
80   //const static int accept_multiple;
81 
82 
83   /**
84    * @param helptext A brief summary of the program's function, to be printed
85    *        when the help flag is detected
86    */
87   ProgOptions( const std::string& helptext = "",
88                const std::string& briefdesc = "" );
89   ~ProgOptions();
90 
91   /** Specify the program version
92    *
93    * Set the program version to a given string.  This will be printed when printVersion()
94    * is called.
95    * @param version_string The version string
96    * @param addflag If true, a default '--version' option will be added.  If false,
97    *        the version will be set, but no option will be added to the parser.
98    */
99   void setVersion( const std::string& version_string, bool addFlag = true );
100 
101   /** Specify a new command-line option
102    *
103    * Instruct the parser to accept a new command-line argument, as well as specifying
104    * how the argument should be handled.  The template parameter indicates the type of
105    * command-line option being specified: acceptable types are void (indicating a flag
106    * without an argument), int, double, and std::string.
107    *
108    * @param namestring The command-line options name(s).  Format is longname,shortname.
109    *        If the comma is omitted, or appears only at the end, this option will have
110    *        no shortname; if the comma is the first letter of the namestring, the option
111    *        has no longname.
112    * @param helpstring The help information displayed for the option when the program is
113    *        invoked with --help
114    * @param value A pointer to memory in which to store the parsed value for this option.
115    *        If NULL, then the value of the option must be queried using the getOpt function.
116    *        If the template parameter is void and value is non-NULL, treat value as a bool*
117    *        and store 'true' into it when the flag is encountered.  (See also store_false, above)
118    * @param flags Option behavior flags, which should come from static vars in the ProgOptions
119    *        class
120    */
121   template <typename T>
122   void addOpt( const std::string& namestring, const std::string& helpstring,
123 	       T* value, int flags = 0 );
124 
125   /** Specify a new command-line option
126    *
127    * This funtion is identical to the 4-arg version, but omits the value parameter, which
128    * is assumed to be NULL
129    */
130   template <typename T>
addOpt(const std::string & namestring,const std::string & helpstring,int flags=0)131   void addOpt( const std::string& namestring, const std::string& helpstring, int flags = 0 ){
132     addOpt<T>( namestring, helpstring, NULL, flags );
133   }
134 
135   /** Add a new line of help text to the option help printout
136    *
137    * Add a line of text to the option-related help.  Called between calls to addOpt(),
138    * this function can be used to divide the option list into groups of related options
139    * to make the help text more convenient.
140    */
141   void addOptionHelpHeading( const std::string& );
142 
143 
144   /** Add required positional argument
145    *
146    * Add a new required positional argument.  The order in which arguments are specified
147    * is the order in which they will be expected on the command line.
148    * The template parameter may be int, double, or std::string (but not void)
149    * @param helpname The name to give the argument in the help text
150    * @param helpstring The help text for the argument
151    * @param value Pointer to where parsed value from command line should be stored.
152    *        If NULL, the value must be queried using getReqArg()
153    */
154   template <typename T>
155   void addRequiredArg( const std::string& helpname, const std::string& helpstring, T* value = NULL, int flags = 0 );
156 
157   /** Add optional positional arguments
158    *
159    * Specify location in ordered argument list at which optional arguments
160    * may occur.  Optional arguments are allowed at only one location
161    * it argument list (this function may not be called more than once.).
162    * The template parameter may be int, double, or std::string (but not void)
163    * @param count The maximum number of optional arguments.  Specify zero for unlimited.
164    * @param helpname The name to give the argument in the help text
165    * @param helpstring The help text for the arguments
166    */
167   template <typename T>
168   void addOptionalArgs( unsigned max_count, const std::string& helpname, const std::string& helpstring, int flags = 0 );
169 
170   /**
171    * Print the full help to the given stream
172    */
173   void printHelp( std::ostream& str = std::cout );
174 
175   /**
176    * Print only the usage message to the given stream
177    */
178   void printUsage( std::ostream& str = std::cout );
179 
180   /**
181    * Print the version string to the given stream
182    */
183   void printVersion( std::ostream& str = std::cout );
184 
185   /**
186    * Parse command-line inputs as given to main()
187    */
188   void parseCommandLine( int argc, char* argv[] );
189 
190   /**
191    *
192    * Get the value of the named option.
193    * @param namestring The name string given when the option was created.  This need not be
194    *        idential to the created name; only the longname, or the shortname (with comma prefix),
195    *        will also work.
196    * @param value Pointer to location to store option argument, if any is found
197    * @return True if the option was set and its argument was stored into value; false otherwise.
198    */
199   template <typename T>
200   bool getOpt( const std::string& namestring, T* value );
201 
202   /**
203    * Get a list of values for the named option-- one value for each time it was
204    * given on the command line.
205    *
206    * This function cannot be called with void as the template parameter;
207    * compilers will reject vector<void> as a type.  This means it cannot be
208    * called for flag-type options.  To count the number of times a given flag
209    * was specified, use numOptSet()
210    * @param namestring See similar argument to getOpt()
211    * @param values Reference to list to store values into.  Will have as many entries
212    *        as there were instances of this option on the command line
213    */
214   template <typename T>
215   void getOptAllArgs( const std::string& namestring, std::vector<T>& values );
216 
217 
218   /**
219    * @param namestring See similar argument to getOpt()
220    * @return The number of times the named option appeared on the command line.
221    */
222   int numOptSet( const std::string& namestring );
223 
224   /**
225    * Retrieve the value of a required command-line argument by name
226    * @param namestring The helpname that was given to addRequiredArg when the
227    *        desired argument was created
228    */
229   template <typename T>
230   T getReqArg( const std::string& namestring );
231 
232   /**
233    * Append the values of any required or optional arguments
234    * @param namestring The helpname that was given to addRequiredArg or
235    *                   addOptionalArgs.
236    */
237   template <typename T>
238   void getArgs( const std::string& namestring, std::vector<T>& values );
239 
240   /**
241    * Prints an error message to std::cerr, along with a brief usage message,
242    * then halts the program.  Used throughout ProgramOptions implementation.
243    * Users may call this directly if they detect an incorrect usage of program
244    * options that the ProgramOptions wasn't able to detect itself.
245    * @param message The error message to print before program halt.
246    */
247   void error( const std::string& message );
248 
249   /**
250    * Write help data formatted for use as a unix man page.
251    */
252   void write_man_page( std::ostream& to_this_stream );
253 
254 protected:
255 
256   std::string get_option_usage_prefix( const  ProgOpt& option );
257 
258   void get_namestrings( const std::string& input, std::string* l, std::string* s );
259 
260   ProgOpt* lookup( const std::map<std::string, ProgOpt* >&, const std::string& );
261   ProgOpt* lookup_option( const std::string& );
262 
263   bool evaluate( const ProgOpt& opt, void* target, const std::string& option, unsigned* arg_idx = NULL);
264   bool process_option( ProgOpt* opt, std::string arg, const char* value = 0 );
265 
266   std::map< std::string, ProgOpt* > long_names;
267   std::map< std::string, ProgOpt* > short_names;
268   std::map< std::string, ProgOpt* > required_args;
269 
270   typedef std::pair<ProgOpt*, std::string> help_line;
271   std::vector< help_line > option_help_strings;
272   std::vector< help_line > arg_help_strings;
273   std::vector< std::string > main_help;
274   std::string brief_help;
275 
276   bool expect_optional_args;
277   unsigned optional_args_position, max_optional_args;
278 
279   std::string progname;
280   std::string progversion;
281 
282     // if an option was specified with the int_flag, this
283     // will contain the long name of the option
284   std::string number_option_name;
285 
286 };
287 
288 #endif /* MOAB_PROGRAM_OPTIONS_H */
289