Is there a really effective way of dealing with command line parameters in C++?
What I'm doing below feels completely amateurish, and I can't imagine this is how command line parameters are really handled (atoi, hard-coded argc checks) in professional software.
// Command line usage: sum num1 num2
int main(int argc, char *argv[])
{
if (argc < 3)
{
cout << "Usage: " << argv[0] << " num1 num2\n";
exit(1);
}
int a = atoi(argv[1]);
int b = atoi(argv[2]);
int sum = a + b;
cout << "Sum: " << sum << "\n";
return 0;
}
You probably want to use an external library for that. There are many to chose from.
Boost has a very feature-rich (as usual) library Boost Program Options.
My personal favorite for the last few years has been TCLAP -- purely templated, hence no library or linking, automated '--help' generation and other goodies. See the simplest example from the docs.
You could use an already created library for this
http://www.boost.org/doc/libs/1_44_0/doc/html/program_options.html
if this is linux/unix then the standard one to use is gnu getopt
http://www.gnu.org/s/libc/manual/html_node/Getopt.html
It's a bit too big to include in a Stack Overflow answer, but I made a library for defining commands lines declaratively. It takes advantage of the the C++14 ability to build up a class constructor by giving initial values to each member variable.
The library is mostly a base class. To define your command syntax, you declare a struct that derives from it. Here's a sample:
struct MyCommandLine : public core::CommandLine {
Argument<std::string> m_verb{this, "program", "program.exe",
"this is what my program does"};
Option<bool> m_help{this, "help", false,
"displays information about the command line"};
Alias<bool> alias_help{this, '?', &m_help};
Option<bool> m_demo{this, "demo", false,
"runs my program in demonstration mode"};
Option<bool> m_maximize{this, "maximize", false,
"opens the main window maximized"};
Option<int> m_loops{this, "loops", 1,
"specifies the number of times to repeat"};
EnumOption<int> m_size{this, "size", 3,
{ {"s", 1},
{"small", 1},
{"m", 3},
{"med", 3},
{"medium", 3},
{"l", 5},
{"large", 5} } };
BeginOptionalArguments here{this};
Argument<std::string> m_file{this, "file-name", "",
"name of an existing file to open"};
} cl;
The Argument
, Option
, and Alias
class templates are declared in the scope of the CommandLine
base class, and you can specialize them for your own types. Each one takes the this
pointer, the option name, the default value, and a description for use in printing the command synopsis/usage.
I'm still looking to eliminate the need to sprinkle all the this
pointers in there, but I haven't found a way to do it without introducing macros. Those pointers allow each member to register itself with the tables in the base class that drive the parsing.
Once you have an instance, there are several overloads of a method to parse the input from a string or a main
-style argument vector. The parser handles both Windows-style and Unix-style option syntax.
if (!cl.Parse(argc, argv)) {
std::string message;
for (const auto &error : cl.GetErrors()) {
message += error + "\n";
}
std::cerr << message;
exit(EXIT_FAILURE);
}
Once it's parsed, you can access the value of any of the options using operator()
:
if (cl.m_help()) { std::cout << cl.GetUsage(); }
for (int i = 0; i < cl.m_loops(); ++i) { ... }
The whole library is only about 300 lines (excluding tests). The instances are a bit bloaty, since the parsing tables are part of the instance (rather than the class). But you generally only need one instance per program, and the convenience of this purely declarative approach is pretty powerful, and an instance can be reset simply by parsing new input.
I would recommend always using boost lexical_cast<>
in place of junk like atoi
, atof
, etc.
http://www.boost.org/doc/libs/release/libs/conversion/lexical_cast.htm
Other than that your code okay for simple stuff.
I am using getopt() under windows/mingw :
while ((c = getopt(myargc, myargv, "vp:d:rcx")) != -1) {
switch (c) {
case 'v': // print version
printf("%s Version %s\n", myargv[0], VERSION);
exit(0);
break;
case 'p': // change local port to listen to
strncpy(g_portnum, optarg, 10);
break;
...