NCBI C++ ToolKit
multi_command.cpp
Go to the documentation of this file.

Go to the SVN repository for this file.

1 /* $Id: multi_command.cpp 54818 2012-06-13 13:35:38Z mcelhany $
2  * ===========================================================================
3  *
4  * PUBLIC DOMAIN NOTICE
5  * National Center for Biotechnology Information
6  *
7  * This software/database is a "United States Government Work" under the
8  * terms of the United States Copyright Act. It was written as part of
9  * the author's official duties as a United States Government employee and
10  * thus cannot be copyrighted. This software/database is freely available
11  * to the public for use. The National Library of Medicine and the U.S.
12  * Government have not placed any restriction on its use or reproduction.
13  *
14  * Although all reasonable efforts have been taken to ensure the accuracy
15  * and reliability of the software and data, the NLM and the U.S.
16  * Government do not and cannot warrant the performance or results that
17  * may be obtained by using this software or data. The NLM and the U.S.
18  * Government disclaim all warranties, express or implied, including
19  * warranties of performance, merchantability or fitness for any particular
20  * purpose.
21  *
22  * Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Authors: Denis Vakatov, Andrei Gourianov, David McElhany
27  *
28  * File Description:
29  *
30  * --- General ---
31  *
32  * This program demonstrates how to set up and process command-line arguments
33  * for programs that support "command-based" command lines instead of, or in
34  * addition to, a "command-less" command line.
35  * - A "command-based" command line begins with a "command" (a case-sensitive
36  * keyword), typically followed by other arguments. See the description of
37  * the "post" command below.
38  * - A "command-less" command line doesn't contain such "commands".
39  * - Programs that support command-based command lines can support any number
40  * of commands (each with its own set of supported arguments), and may
41  * optionally support a command-less command line as well.
42  *
43  * To demonstrate setting up and processing command-based usage forms in the
44  * most clear and concise way, this program does not demonstrate the basics of
45  * command-less command-line argument processing. If you'd like to see how to
46  * set up optional and mandatory flags, keys, positional arguments, etc.,
47  * please see basic_sample.cpp.
48  *
49  * It is assumed that the reader is familiar with the NCBI application
50  * framework class CNcbiApplication and with auto_ptr.
51  *
52  * --- Program Invocation and Supported Features ---
53  *
54  * Here are the command-based usage forms this program supports:
55  * multi_command list
56  * multi_command create <queue>
57  * multi_command post <queue> [-imp importance] <message>
58  * multi_command query [queue]
59  *
60  * Here are some examples of possible command-based invocations:
61  * multi_command create myqueue-101
62  * multi_command list
63  * multi_command post myqueue-101 -imp URGENT "Stop all operations!"
64  * multi_command query
65  *
66  * With the "list" command, no additional arguments are necessary.
67  * With "create", the "queue" positional argument is required.
68  * With "query", the "queue" positional argument is optional.
69  *
70  * The "post" command illustrates a new feature introduced with command-based
71  * command lines - opening arguments. Opening arguments are essentially
72  * identical to mandatory positional arguments except that opening arguments
73  * must precede optional arguments whereas mandatory positional arguments
74  * must follow them. Thus, opening arguments allow usage forms such as the
75  * "post" command in this program, which has an optional argument between
76  * mandatory arguments.
77  *
78  * In addition to the commands listed above, this program also supports this
79  * this command-less usage form:
80  * multi_command [-v] <script_file>
81  *
82  * Here is an example invocation using a command-less command line:
83  * multi_command -v routine_maint.cmds
84  *
85  * --- Required Steps to Process Command-Based Command-Lines ---
86  *
87  * At a high level, setting up a program to support a command-less command-line
88  * requires creating a CArgDescriptions object, adding argument descriptions
89  * to it, and passing it to SetupArgDescriptions().
90  *
91  * Setting up a program to support command-based command lines is similar, but
92  * requires a CCommandArgDescriptions instead. The CCommandArgDescriptions
93  * class is derived from CArgDescriptions, so all the same functionality is
94  * available; however, the AddCommand() method of CCommandArgDescriptions
95  * allows you to create multiple CArgDescriptions objects (one for each
96  * command) in addition to the overall program description. Other command-
97  * specific features are also provided, such as command grouping and
98  * opening arguments.
99  *
100  * Programs that support command-based command lines must execute these steps:
101  * 1. Create a command descriptions object (class CCommandArgDescriptions)
102  * for the overall program description.
103  * 2. Create argument descriptions objects (class CArgDescriptions) for each
104  * command.
105  * 3. Add the actual argument descriptions to the argument descriptions
106  * objects using methods such as AddOpening(), AddPositional(), etc.
107  * 4. Add each argument descriptions object to the overall command
108  * descriptions object.
109  * 5. Determine which command was specified on the command line.
110  * 6. Process the appropriate arguments for the given command.
111  *
112  * --- Program Organization ---
113  *
114  * The first four required steps above could be done in one long sequence of
115  * statements in the application's Init() function. However, this could become
116  * unwieldy for programs with many commands. Factoring those steps into
117  * dedicated "Init" functions for each command keeps the application's Init()
118  * function readable.
119  *
120  * Similarly, the sixth required step could be implemented as an "if-else-if"
121  * type construct in the application's Run() function, with a branch for each
122  * command. However, a cleaner approach is to create a dedicated "Run"
123  * function for each command.
124  *
125  * This program is arranged in essentially three sections (four if you include
126  * this rather long introductory comment):
127  * 1. Command-specific "Init" and "Run" functions. This section comes first
128  * because pointers to these functions are used in the next section. You
129  * may want to read this section last.
130  * 2. Constructs for simplifying the program - the command "metadata".
131  * 3. The application's class definition.
132  */
133 
134 #include <ncbi_pch.hpp>
135 #include <corelib/ncbiapp.hpp>
136 #include <corelib/ncbiargs.hpp>
137 
138 #include <common/test_assert.h> /* This header must go last, if used at all */
139 
141 
142 
143 /////////////////////////////////////////////////////////////////////////////
144 // "Init" and "Run" functions for each command
145 //
146 // The "Init" functions add the command-specific argument descriptions.
147 // These descriptions are used when parsing the command line and when
148 // generating the USAGE statement.
149 //
150 // The "Run" functions use the arguments set up by the corresponding "Init"
151 // functions to execute the command-specific functionality.
152 
153 
154 // Note: The "list" command doesn't have any additional arguments, so it
155 // doesn't have an "Init" function.
156 
157 static int s_RunList(const CArgs & args, ostream & os)
158 {
159  // The "list" command might print a list of queue names.
160  os << "Here is the list of queues: ..." << endl;
161  return 0;
162 }
163 
164 
165 static void s_InitCreate(CArgDescriptions & arg_desc)
166 {
167  // The "create" command requires the "queue" argument. Here it is being
168  // added as an opening argument - a mandatory argument preceding any
169  // other arguments.
170  arg_desc.AddOpening("queue", "Queue name", CArgDescriptions::eString);
171 
172  // Note that because the "queue" argument isn't followed by any optional
173  // arguments, it could just as well have been added as a positional
174  // argument:
175  //arg_desc.AddPositional("queue", "Queue name", CArgDescriptions::eString);
176 }
177 
178 static int s_RunCreate(const CArgs & args, ostream & os)
179 {
180  // The "create" command might create a queue with a given name.
181  os << "Creating queue '" << args["queue"].AsString() << "'." << endl;
182  return 0;
183 }
184 
185 
186 static void s_InitPost(CArgDescriptions & arg_desc)
187 {
188  // The "post" command requires the "queue" opening argument.
189  // AddOpening must be used in this case because the queue argument precedes
190  // a non-positional argument.
191  arg_desc.AddOpening("queue", "Queue name", CArgDescriptions::eString);
192 
193  // The "post" command allows the "imp" key and requires the "message"
194  // positional argument.
195  arg_desc.AddOptionalKey("imp", "Importance", "The message importance",
197  arg_desc.AddPositional("message", "The message to post",
199 }
200 
201 static int s_RunPost(const CArgs & args, ostream & os)
202 {
203  // The "post" command might send a message to a queue, with an optional
204  // importance value.
205  os << "Sending message '" << args["message"].AsString()
206  << "' to queue '" << args["queue"].AsString() << "'";
207  if ( args["imp"].HasValue() ) {
208  os << " with importance '" << args["imp"].AsString() << "'." << endl;
209  } else {
210  os << " with default importance." << endl;
211  }
212  return 0;
213 }
214 
215 
216 static void s_InitQuery(CArgDescriptions & arg_desc)
217 {
218  // The "query" command allows the "queue" argument. Because it is optional,
219  // it must be a positional argument, not an opening argument.
220  arg_desc.AddOptionalPositional("queue", "Queue name",
222 }
223 
224 static int s_RunQuery(const CArgs & args, ostream & os)
225 {
226  // The "query" command might print the messages for a given queue, or for
227  // all queues if none was specified.
228  if ( args["queue"].HasValue() ) {
229  os << "Messages for queue '" << args["queue"].AsString() << "': ..."
230  << endl;
231  } else {
232  os << "Messages for all queues: ..." << endl;
233  }
234  return 0;
235 }
236 
237 
238 // The following "Init" and "Run" functions are for the command-less usage
239 // form of the program, i.e. multi_command [-v] <script_file>
240 
241 static void s_CommandlessInit(CArgDescriptions & arg_desc)
242 {
243  // Example arguments for a command-less usage form:
244  arg_desc.AddFlag("v", "Verbose");
245  arg_desc.AddPositional("script_file", "A file containing commands",
247 }
248 
249 static int s_CommandlessRun(const CArgs & args, ostream & os)
250 {
251  // The command-less usage form would do something with the arguments.
252  os << "Executing script file '" << args["script_file"].AsString() << "'."
253  << endl;
254  if ( args["v"].AsBoolean() ) {
255  os << " Blah, blah, blah..." << endl;
256  }
257  return 0;
258 }
259 
260 
261 /////////////////////////////////////////////////////////////////////////////
262 // Convenience constructs
263 
264 // Function pointer types for the "Init" and "Run" functions.
265 typedef void (*FInit)(CArgDescriptions & arg_desc);
266 typedef int (*FRun)(const CArgs & args, ostream & os);
267 
268 // A structure to collect all the metadata for a command.
269 struct SCmd {
270  string m_Name; // the command's name - really, the command itself
271  string m_Desc; // its description
272  string m_Alias; // an alias
273  FInit m_Init; // the command's "Init" function
274  FRun m_Run; // the command's "Run" function
275 };
276 
277 // Specify the metadata and function pointers for all commands.
278 //
279 // The following array of structures collects the basic information about all
280 // the commands used in this program in one place. Using an array of structures
281 // like this is not a requirement for processing command-based arguments, but
282 // it greatly simplifies both the specification of the metadata and the
283 // application's Init() method. Alternatively, you could repeat the required
284 // steps - currently in a for loop in Init() - for each command, supplying the
285 // metadata as needed in each repeated group of statements.
286 
287 static SCmd s_Cmds[] = {
288  //name description alias "Init" function "Run" function
289  { "list", "list entries", "lst", NULL, s_RunList },
290  { "create", "create an entry", "crt", s_InitCreate, s_RunCreate },
291  { "post", "post an entry", "pst", s_InitPost, s_RunPost },
292  { "query", "select entries", "qry", s_InitQuery, s_RunQuery }
293 };
294 static size_t s_NumCmds = sizeof(s_Cmds) / sizeof(s_Cmds[0]);
295 
296 
297 /////////////////////////////////////////////////////////////////////////////
298 // CMultiCommandApplication::
299 
301 {
302 private:
303  virtual void Init(void);
304  virtual int Run(void);
305 };
306 
307 
308 // Set up the argument descriptions for all commands.
310 {
311  // Create the program's overall command-line argument descriptions class.
312  // This will be a container for both the command-less and command-based
313  // usage forms.
314  // The ECommandPresence parameter controls whether or not the user must
315  // enter a command-based command line. Use eCommandOptional only when you
316  // are setting up both command-less and command-based command lines.
318  new CCommandArgDescriptions(true, 0,
320 
321  // Specify the overall program USAGE context.
322  cmd_desc->SetUsageContext(GetArguments().GetProgramBasename(),
323  "Demo program for command-based command-line "
324  "argument processing.", false);
325 
326  // Set up the argument descriptions for the command-less command line.
327  s_CommandlessInit(*cmd_desc);
328 
329  // The following loop sets up the separate commands.
330  for (size_t idx = 0; idx < s_NumCmds; ++idx) {
331 
332  // Create a container for this command's arguments. Argument
333  // descriptions are allocated on the heap.
335 
336  // Describe this command (the name parameter is not used for commands).
337  // This description will be shown in the USAGE statement.
338  arg_desc->SetUsageContext("", s_Cmds[idx].m_Desc);
339 
340  // Set up this command's argument descriptions (if it has any) using
341  // its "Init" function.
342  if ( s_Cmds[idx].m_Init != NULL ) {
343  s_Cmds[idx].m_Init(*arg_desc);
344  }
345 
346  // OPTIONAL: Add this command to a group of commands. Command groups
347  // serve the sole purpose of organizing the commands in the USAGE
348  // statement.
349  // if ( this_command_should_be_grouped ) {
350  // cmd_desc->SetCurrentCommandGroup(the_group_name);
351  // }
352 
353  // Add this command (and its argument descriptions) to the program's
354  // overall argument descriptions object, 'cmd_desc'. Ownership is
355  // transfered to 'cmd_desc' by calling the auto_ptr's release() method
356  // and passing that to 'cmd_desc' via AddCommand().
357  cmd_desc->AddCommand(s_Cmds[idx].m_Name, arg_desc.release(),
358  s_Cmds[idx].m_Alias);
359  }
360 
361  // Finalize the setup for all of the program's argument descriptions
362  // (also transferring ownership of the argument descriptions).
363  //
364  // Note: This is also where the supplied command line is checked, and if
365  // it doesn't match a valid usage form, an exception will be thrown.
366  SetupArgDescriptions(cmd_desc.release());
367 }
368 
369 
370 // Process the arguments obtained from the command line.
372 {
373  // Get the specified command (if one is supplied).
374  string command(GetArgs().GetCommand());
375 
376  // Check for the command-less usage form.
377  if ( command == "" ) {
378  return s_CommandlessRun(GetArgs(), cout);
379  }
380 
381  // A command-based usage form was used, so call the command's "Run"
382  // function.
383  for (size_t idx = 0; idx < s_NumCmds; ++idx) {
384  if ( command == s_Cmds[idx].m_Name ) {
385  _ASSERT(s_Cmds[idx].m_Run != NULL); // empty "Run" is meaningless
386  return s_Cmds[idx].m_Run(GetArgs(), cout);
387  }
388  }
389 
390  // Should never get here. If the command line is not valid, an exception
391  // will be thrown in Init(). If it is valid, then it will either result in
392  // s_CommandlessRun() or one of the command's "Run" functions being called.
393  _ASSERT(false);
394  return 1;
395 }
396 
397 
398 int main(int argc, const char * argv[])
399 {
400  return CMultiCommandApplication().AppMain(argc, argv);
401 }
void AddCommand(const string &cmd, CArgDescriptions *description, const string &alias=kEmptyStr)
Add command argument descriptions.
Definition: ncbiargs.cpp:3761
CCommandArgDescriptions –.
Definition: ncbiargs.hpp:1286
int main(int argc, const char *argv[])
static int s_RunQuery(const CArgs &args, ostream &os)
static void s_InitCreate(CArgDescriptions &arg_desc)
Defines command line argument related classes.
virtual void SetupArgDescriptions(CArgDescriptions *arg_desc)
Setup the command line argument descriptions.
Definition: ncbiapp.cpp:779
virtual int Run(void)
Run the application.
static void s_CommandlessInit(CArgDescriptions &arg_desc)
#define NULL
Definition: ncbistd.hpp:225
int AppMain(int argc, const char *const *argv, const char *const *envp=0, EAppDiagStream diag=eDS_Default, const char *conf=NcbiEmptyCStr, const string &name=NcbiEmptyString)
Main function (entry point) for the NCBI application.
Definition: ncbiapp.cpp:492
void AddOptionalPositional(const string &name, const string &comment, EType type, TFlags flags=0)
Add description for optional positional argument without default value.
Definition: ncbiargs.cpp:2294
X * release(void)
Release pointer.
Definition: ncbimisc.hpp:365
int(* FRun)(const CArgs &args, ostream &os)
virtual void Init(void)
Initialize the application.
static void s_InitPost(CArgDescriptions &arg_desc)
static int s_RunCreate(const CArgs &args, ostream &os)
FInit m_Init
void AddPositional(const string &name, const string &comment, EType type, TFlags flags=0)
Add description for mandatory postional argument.
Definition: ncbiargs.cpp:2268
auto_ptr –
Definition: ncbimisc.hpp:301
void AddFlag(const string &name, const string &comment, bool set_value=true)
Add description for flag argument.
Definition: ncbiargs.cpp:2256
static int s_CommandlessRun(const CArgs &args, ostream &os)
FRun m_Run
USING_NCBI_SCOPE
virtual const CArgs & GetArgs(void) const
Get parsed command line arguments.
Definition: ncbiapp.cpp:185
CArgDescriptions –.
Definition: ncbiargs.hpp:503
static int s_RunPost(const CArgs &args, ostream &os)
static void s_InitQuery(CArgDescriptions &arg_desc)
static SCmd s_Cmds[]
An arbitrary string.
Definition: ncbiargs.hpp:552
const CNcbiArguments & GetArguments(void) const
Get the application's cached unprocessed command-line arguments.
Definition: ncbiapp.hpp:649
CArgs –.
Definition: ncbiargs.hpp:354
const char * command
Defines the CNcbiApplication and CAppException classes for creating NCBI applications.
static int s_RunList(const CArgs &args, ostream &os)
CNcbiApplication –.
Definition: ncbiapp.hpp:120
void SetUsageContext(const string &usage_name, const string &usage_description, bool usage_sort_args=false, SIZE_TYPE usage_width=78)
Set extra info to be used by PrintUsage().
Definition: ncbiargs.cpp:3081
string m_Name
#define _ASSERT
void AddOpening(const string &name, const string &comment, EType type, TFlags flags=0)
Add description of mandatory opening positional argument.
Definition: ncbiargs.cpp:2281
void(* FInit)(CArgDescriptions &arg_desc)
string m_Alias
void AddOptionalKey(const string &name, const string &synopsis, const string &comment, EType type, TFlags flags=0)
Add description for optional key without default value.
Definition: ncbiargs.cpp:2223
string m_Desc
static size_t s_NumCmds
unsigned int
Definition: types.hpp:1068
Modified on Sun Jul 05 13:08:13 2015 by modify_doxy.py rev. 426318