The previous method for passing in values is generally fine if a single value is being passed in. Suppose we have a similar program for interacting with a rectangle. We could pass in the height and width as command line arguments. But the user would need to remember the correct order to enter them i.e. height width or width height. Some programs may even have 10 or more inputs. How can the user be expected to remember the correct order? It is a sure fire way of entering incorrect data.
We can prevent this by using flags. These are very common and indeed, you have come across them already when using g++. In that case, we specify the output file using the -o flag.
The example below demonstrates how we could implement a flag system for our circle program.
main.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include <cmath> #include <iostream> const double PI = 3.14159265359; void parse_flags(int argc, char const *argv[]); void print_usage(char const *argv[]); void print_version(); int main(int argc, char const *argv[]) { if (argc == 1) { std::cerr << "\nERROR! No flags supplied! \n"; print_usage(argv); return 1; } parse_flags(argc, argv); return 0; } void parse_flags(int argc, char const *argv[]) { // this flag is used to check for valid input bool valid = false; // start at 1 so not to include the program name for (int i = 1; i < argc; i++) { // check for valid flags, use if,if,if rather than if,else if so we can // supply multiple (valid) flags if (std::string(argv[i]) == "-h" || std::string(argv[i]) == "--help") { print_usage(argv); valid = true; } if (std::string(argv[i]) == "-v" || std::string(argv[i]) == "--version") { print_version(); valid = true; } if (std::string(argv[i]) == "-r" || std::string(argv[i]) == "--radius") { valid = true; // need to make sure a value has been entered as well as -r if (i + 1 < argc) { double radius = atof(argv[i + 1]); double area = PI * pow(radius, 2.0); std::cout << "\nCircle area = " << area << " m^2.\n\n"; } else { std::cerr << "\nERROR! -r option requires radius value!\n\n"; print_usage(argv); exit(1); } } } // if not come across valid flag, then print usage instructions and exit if (valid == false) { std::cerr << "\nERROR! Invalid flags supplied!\n\n"; print_usage(argv); exit(1); } } void print_usage(char const *argv[]) { std::cout << "\nUsage: " << argv[0] << " FLAGS\n\n"; std::cout << "Flags:\n\n-r,--radius RADIUS\tinput circle radius.\n"; std::cout << "-h,--help\t\tprint usage instructions.\n"; std::cout << "-v,--version\t\tprint version number.\n\n"; } void print_version() { std::cout << "\nVersion 1.0.1\n"; } |
This example has several functions. In the parse_flags function, we loop through the flags and check for hard-coded options. The possible options are help, version number and for entering the radius. Note that each option has two ways of entering the flag e.g. -h or --help for printing the usage instructions. This is a common way of implementing flags. Depending on which flag is entered, then the relevant function is called. Note that a boolean flag is used. This is set to 'true' when a valid flag is entered. If no valid flag is entered, it will remain false. This boolean flag is checked and used to print an error message.
Some usage scenarios are shown below.
No command line arguments entered.
./main.exe
ERROR! No flags supplied! Usage: ./main.exe FLAGS Flags: -r,--radius RADIUS input circle radius. -h,--help print usage instructions. -v,--version print version number.
Entering an incorrect flag
./main.exe --wrong
ERROR! Invalid flags supplied! Usage: ./main.exe FLAGS Flags: -r,--radius RADIUS input circle radius. -h,--help print usage instructions. -v,--version print version number.
Printing the version number
./main.exe -v
./main.exe --version
Version 1.0.1
Asking for usage instructions.
./main.exe -h
./main.exe --help
Usage: ./main.exe FLAGS Flags: -r,--radius RADIUS input circle radius. -h,--help print usage instructions. -v,--version print version number.
Entering a radius.
./main.exe --radius 1.0
./main.exe -r 1.0
Circle area = 3.14159 m^2.
Note that multiple flags can be used on the command line at any one time as we have used multiple if statements in the parse_flags function. If an if, else if, else construct had been used, as soon as a valid flag was detected, the rest would have been ignored. Finally, note the use of blank lines and tabs in order to nicely format the usage instructions and make it easier for the user to read them.