Using Macros and Command Line Arguments with C++
Image by Christopher Kuszajewski from Pixabay
Macros are a very useful feature of the C++ Programming Language that made their first appearance in the C programming language decades ago in the early years of Unix and C. Macros can be used equally well in either C++ or C.
Macros allow a developer to designate constant values and also to create function like constants that can handle parameters. Macros are resolved at compile time to their definition, and are usually designated by all capital letters, which is the normal style for them.
In this blog post, we will build a simple C++ program demonstrating the use of macros, and also, demonstrating the use of command line arguments. In the early years of C programming on Unix operating systems, then later with C++, before the appearance of Graphical User Interfaces (GUIs), programs were run from the Unix command line, commonly referred to as the CLI.
Using Macros for Simple Constants
A good demonstration of using macros is for the mathematical constant of PI, an infinite number. Enter the following lines of code at the beginning of our C++ program, which we will name macroexample.cpp
#ifndef PI #include <iostream> #include <cstdlib> #include <string> using namespace std; #define PI 3.1415926535
The first line is an IF statement that tells the program to define the macros only if they are not already defined. Then the DEFINE statement is used to define the value of PI. At compile time, the compiler will replace the word PI with the number value it represents. Normally, macros are always in capital letters so they are easy to identify. Here is an example of how we would use our PI constant:
// constant PI cout << "PI: " << PI << endl;
This code will output the number 3.1415926535 every time the word PI is encountered in the code. This is very useful as you do not have to enter the number for PI every time you want to use it. And also, macros work well for other constants, like that of the Earth's gravity, 9.80665 m/s 2.
Building Functions Using Macros
Macros can also be used to define functions. And also, process input parameters like a normal function. Macros can also be reused inside of other macros. In this next example, we are reusing PI to define macros that calculate the Area and Circumference of a circle with an input parameter r, the radius of the circle:
#define AREA(r) (PI*(r)*(r)) #define CIRCUMFERENCE(r) (PI*(2)*(r))
Note the use of parentheses around the function, this is very important to prevent undefined behavior happening with the macros. Here is an example of how we would use these macros to output the Area and Circumference of a circle with radius r of 5 inches:
// area of a circle cout << "AREA CIRCLE radius=5 " << AREA(5) << endl; // circumference of a circle cout << "CIRCUMFERENCE CIRCLE radius=5 " << CIRCUMFERENCE(5) << endl;
Using our macros to find the Area and Circumference of a circle of radius 5 inches, we would get an Area of 78.5398 square inches, and a Circumference of 31.4159 inches.
Using Macros To Define Multi-line Functions
So far our macro definitions have been only a single line, but it is very simple to have a multi-line definition for a macro. To do that, you just end each line with a slash \. We will do this in our example C++ program with a new macro function definition to print out a number of lines, like the following C++ code. Important Note, since we used the #ifndef directive at the start of our macro definitions, we also have to end the macro definition section with the statement #endif
#define PRINTLINES(count) \ for (int i=1;i <= count;i++) { \ cout << "Line " << i << endl; \ } #endif
This macro will print a number of lines based upon the input parameter COUNT. Here is an example of how we would use this macro to print 5 lines.
PRINTLINES(5);
This is the output of our PRINTLINES macro when the number 5 is passed in as a parameter:
Line 1
Line 2
Line 3
Line 4
Line 5
Using Command Line Arguments With C++
In the early years of the Unix operating system and the C Programming Language, and then later with C++, Graphical User Interfaces (GUIs) had not yet been developed, and the primary interaction with Unix was from the Command Line Interface (CLI). The program that allowed a user to interact with the computer at the CLI is called a SHELL. Shell statements are able to be placed together in a program called a Shell Script. Shells are still a standard way to interact with Unix computers, and now Linux computers, even after the development of GUIs like Gnome, KDE, and the Common Desktop Environment (CDE). Currently, one of the most popular shells is the BASH shell. A standard method was included in the C Programming Language, and later in C++, to process command line arguments.
In both C, and also C++, processing of command line arguments is enabled by the addition of several parameters to the MAIN method, like the following:
int main(int argc, char** argv) { return 0; }
The parameter ARGC is the count of the number of command line arguments, and the parameter ARGV is an array of char arrays used for strings. In the early years of the C programming language, strings would be processed using a CHAR array with the name of the variable being a pointer to the char array. Since that time, in C++ now there is a regular data type STRING which is simpler to use than a char array. But char arrays are still widely used in both C and C++ programming.
The strings that make up command line arguments are char array strings, and are referenced using array format. The first index in an array is always 0, the second index 1, the third index 2, and so forth. To get the first command line argument, argv[0] is used. The second command line argument is referenced by argv[1]. Here is an example of how we would output three command line arguments of our C++ program using the ARGV variable that is the second parameter of the MAIN method:
// first command line argument cout << "First Command Line Argument " << argv[0] << endl; // second command line argument cout << "Second Command Line Argument " << argv[1] << endl; // third command line argument cout << "Third Command Line Argument " << argv[2] << endl;
All command line arguments passed into a C or C++ program are char array strings, even numbers. In our example C++ program listed at the end of this blog post, there is a function that we use to test that the char array string is a number, using the ISDIGIT function. Here is the function from our C++ program to test if a char array is a number:
// function to check if number input is an actual number bool isNumber(char* input) { // get the length of the char array string int len = *(&input +1) - input; // loop through the char array // checking each index is a digit for (int i=0;i < len;i++) { if (isdigit(input[i]) == false) { return false; } } return true; }
A simple function to convert a char array number into a regular number type is the ATOI function. Note this example of how it is used with the second command line argument in our C++ program, the radius of a circle.
double radius = atoi(argv[1]);
Before trying to run our C++ example program, we have to compile it using the g++ compiler, or alternately, the CLANG compiler. Use the following statement to compile our C++ program, macroexample.cpp, with the -o argument to name our executable program EXAMPLE.
$ g++ macroexample.cpp -o example
Next, we run our example program running the following at the command line. The first command line argument, argv[0], is the name of the program EXAMPLE, the second command line argument, argv[1], is the radius of our circle r, in this case 5, and the third command line argument, argv[2], is the unit of measurement we are using with our circle, in this case inches:
$ ./example 5 inches PI: 3.14159 AREA CIRCLE radius=5 inches, 78.5398 square inches CIRCUMFERENCE CIRCLE radius=5 inches, 31.4159 inches
Here is a copy-and-pastable listing of our entire C++ program
// macroexample.cpp // // by Michael G. Workman // // This C++ program is freely distributable // under terms of MIT open source license // https://opensource.org/licenses/MIT #ifndef PI #include <iostream> #include <cstdlib> #include <string> using namespace std; #define PI 3.1415926535 #define AREA(r) (PI*(r)*(r)) #define CIRCUMFERENCE(r) (PI*(2)*(r)) #define PRINTLINES(count) \ for (int i=1;i <= count;i++) { \ cout << "Line " << i << endl; \ } #endif // function to check if number input is an actual number bool isNumber(char* input) { // get the length of the char array string int len = *(&input +1) - input; // loop through the char array // checking each index is a digit for (int i=0;i < len;i++) { if (isdigit(input[i]) == false) { return false; } } return true; } int main(int argc, char** argv) { // check to make sure argument format is correct bool errorPresent = false; // check that there is at least 3 arguments if (argc < 3) { errorPresent = true; } // check that the second argument is a valid number // remember with arrays the first index is 0 if ((!errorPresent) && (!isNumber(argv[1]))) { errorPresent = true; } // check that the third argument is a valid unit if ((!errorPresent) && (strcmp(argv[2],"inches") != 0) && (strcmp(argv[2],"centimeters") != 0) && (strcmp(argv[2],"yards") != 0) && (strcmp(argv[2],"meters") != 0)) { errorPresent = true; } // output the proper format if there is an error // return an error code if (errorPresent) { cout << endl << "Format:" << endl; cout << "./example [number length of radius] [unit of measurement]" << endl; cout << "Valid Units of Measurement: inches centimeters yards meters" << endl << endl; // return a non-zero number for an error return 1; } // unit of measurement char* units = argv[2]; // radius of the circle double radius = atoi(argv[1]); // get the area of the circle double area = AREA(radius); // get the circumference of the circle double circumference = CIRCUMFERENCE(radius); // constant PI cout << "PI: " << PI << endl; // area of a circle cout << "AREA CIRCLE radius=" << radius << " " << units << ", " << area << " square " << units << endl; // circumference of a circle cout << "CIRCUMFERENCE CIRCLE radius=" << radius << " " << units << ", " << circumference << " " << units << endl; // convert units if (strcmp(units,"yards") == 0) { cout << "AREA in inches squared: " << area*36*36 << endl; cout << "CIRCUMFERENCE in inches: " << circumference*36 << endl; } else if (strcmp(units, "meters") == 0) { cout << "AREA in centimeters squared: " << area*100*100 << endl; cout << "CIRCUMFERENCE in centimeters: " << circumference*100 << endl; } // print out number of lines double numberLines = atoi(argv[1]); cout << "Print Out " << numberLines << " Lines" << endl; PRINTLINES(numberLines); // when we get to this point it means there were no errors // so we return 0 for no errors return 0; }
I hope this blog post has been useful for anyone wanting to use macros and command line arguments in C++.
For those who wish to try out and learn Unix, there is a NetBSD Unix shell account available from SDF at http://sdf.org for $9 every 3 months. Also, a free OpenBSD Unix shell account is available on https://tilde.institute. There is free Linux offerings on the internet as well. A free Ubuntu Linux shell account is available at https://tilde.team. Many Linux versions are available as free downloads on the internet as well.
To see my Curriculum Vitae, go to www.michaelgworkman.com
To see my projects on Azure DevOps, go to https://dev.azure.com/AbionTechnology/
To see my Posts and Answers on Stack Overflow,
go to Michael G. Workman on Stack Overflow
If you have any questions about C, C++, Microsoft C# .NET, Microsoft Azure Cloud, Unix, Linux, and/or Mobile Apps, please feel free to contact me by email at:
michael@abion.net