Using C++ language features
Let's now use the features you have learned in this chapter to write an application. This example is a simple command-line calculator; you type an expression such as 6 * 7
, and the application parses the input and performs the calculation.
Start Visual C++ and click the File
menu, and then New
, and finally, click on the File...
option to get the New File
dialog. In the left-hand pane, click on Visual C++
, and in the middle pane, click on C++ File (.cpp),
and then click on the Open
button. Before you do anything else, save this file. Using a Visual C++ console (a command line, which has the Visual C++ environment), navigate to the Beginning_C++
folder and create a new folder called Chapter_02
. Now, in Visual C++, on the File
menu, click Save Source1.cpp As...
and in the Save File As
dialog locate the Chapter_02
folder you just created. In the File name
box, type calc.cpp
and click on the Save
button.
The application will use std::cout
and std::string
; so at the top of the file, add the headers that define these and, so that you do not have to use fully qualified names, add a using
statement:
#include <iostream> #include <string> using namespace std;
You will pass the expression via the command-line, so add a main
function that takes command line parameters at the bottom of the file:
int main(int argc, char *argv[]) { }
The application handles expressions in the form arg1 op arg2
where op
is an operator and arg1
and arg2
are the arguments. This means that, when the application is called, it must have four parameters; the first is the command used to start the application and the last three are the expression. The first code in the main
function should ensure that the right number of parameters is provided, so at the top of this function add a condition, as follows:
if (argc != 4) { usage(); return 1; }
If the command is called with more or less than four parameters, a function usage
is called, and then the main
function returns, stopping the application.
Add the usage
function before the main
function, as follows:
void usage() { cout << endl; cout << "calc arg1 op arg2" << endl; cout << "arg1 and arg2 are the arguments" << endl; cout << "op is an operator, one of + - / or *" << endl; }
This simply explains how to use the command and explains the parameters. At this point, you can compile the application. Since you are using the C++ Standard Library, you will need to compile with support for C++ exceptions, so type the following at the command-line:
C:\Beginning_C++Chapter_02\cl /EHsc calc.cpp
If you typed in the code without any mistakes, the file should compile. If you get any errors from the compiler, check the source file to see if the code is exactly as given in the preceding code. You may get the following error:
'cl' is not recognized as an internal or external command,
operable program or batch file.
This means that the console is not set up with the Visual C++ environment, so either close it down and start the console via the Windows Start menu, or run the vcvarsall.bat
batch file.
Once the code has compiled you may run it. Start by running it with the correct number of parameters (for example, calc 6 * 7
), and then try it with an incorrect number of parameters (for example, calc 6 * 7 / 3
). Note that the space between the parameters is important:
C:\Beginning_C++Chapter_02>calc 6 * 7
C:\Beginning_C++Chapter_02>calc 6 * 7 / 3
calc arg1 op arg2
arg1 and arg2 are the arguments
op is an operator, one of + - / or *
In the first case, the application does nothing, so all you see is a blank line. In the second example, the code has determined that there are not enough parameters, and so it prints the usage information to the console.
Next, you need to do some simple parsing of the parameters to check that the user has passed valid values. At the bottom of the main
function, add the following:
string opArg = argv[2]; if (opArg.length() > 1) { cout << endl << "operator should be a single character" << endl; usage(); return 1; }
The first line initializes a C++ std::string
object with the third command-line parameter, which should be the operator in the expression. This simple example only allows a single character for the operator, so the subsequent lines check to make sure that the operator is a single character. The C++ std::string
class has a member function called length
that returns the number of characters in the string.
The argv[2]
parameter will have a length of at least one character (a parameter with no length will not be treated as a command-line parameter!), so we have to check if the user typed an operator longer than one character.
Next you need to test to ensure that the parameter is one of the restricted set allowed and, if the user types another operator, print an error and stop the processing. At the bottom of the main
function, add the following:
char op = opArg.at(0); if (op == 44 || op == 46 || op < 42 || op > 47) { cout << endl << "operator not recognized" << endl; usage(); return 1; }
The tests are going to be made on a character, so you need to extract this character from the string
object. This code uses the at
function, which is passed the index of the character you need. (Chapter 5, Using the Standard Library Containers, will give more details about the members of the std::string
class.) The next line checks to see if the character is not supported. The code relies on the following values for the characters that we support:
Character | Value |
|
|
|
|
|
|
|
|
As you can see, if the character is less than 42
or greater than 47
it will be incorrect, but between 42
and 47
there are two characters that we also want to reject: ,
(44
) and .
(46
). This is why we have the preceding conditional: "if the character is less than 42 or greater than 47
, or it is 44
or 46
, then reject it."
The char
data type is an integer, which is why the test uses integer literals. You could have used character literals, so the following change is just as valid:
if (op == ',' || op == '.' || op < '+' || op > '/')
{
cout << endl << "operator not recognized" << endl;
usage();
return 1;
}
You should use whichever you find the most readable. Since it makes less sense to check whether one character is greater than another, this book will use the former.
At this point, you can compile the code and test it. First try with an operator that is more than one character (for example, **
) and confirm that you get the message that the operator should be a single character. Secondly, test with a character that is not a recognized operator; try any character other than +
, *
, -
, or /
, but it is also worth trying .
and ,
.
Bear in mind that the command prompt has special actions for some symbols, such as "&
" and "|
", and the command prompt may give you an error from it by parsing the command-line before even calling your code.
The next thing to do is to convert the arguments into a form that the code can use. The command-line parameters are passed to the program in an array of strings; however, we are interpreting some of those parameters as floating-point numbers (in fact, double-precision floating-point numbers). The C runtime provides a function called atof
, which is available through the C++ Standard Library (in this case, <iostream>
includes files that include <cmath>
, where atof
is declared).
Note
It is a bit counter-intuitive to get access to a math function such as atof
through including a file associated with stream input and output. If this makes you uneasy, you can add a line after the include
lines to include the <cmath>
file. The C++ Standard Library headers have been written to ensure that a header file is only included once, so including <cmath>
twice has no ill effect. This was not done in the preceding code, because it was argued that atof
is a string function and the code includes the <string>
header and, indeed, <cmath>
is included via the files the <string>
header includes.
Add the following lines to the bottom of the main
function. The first two lines convert the second and fourth parameters (remember, C++ arrays are zero-based indexed) to double
values. The final line declares a variable to hold the result:
double arg1 = atof(argv[1]); double arg2 = atof(argv[3]); double result = 0;
Now we need to determine which operator was passed and perform the requested action. We will do this with a switch
statement. We know that the op
variable will be valid, and so we do not have to provide a default
clause to catch the values we have not tested for. Add a switch
statement to the bottom of the function:
double arg1 = atof(argv[1]); double arg2 = atof(argv[3]); double result = 0; switch(op) { }
The first three cases, +
, -
, and *
, are straightforward:
switch (op) { case '+': result = arg1 + arg2; break; case '-': result = arg1 - arg2; break; case '*': result = arg1 * arg2; break; }
Again, since char
is an integer, you can use it in a switch
statement, but C++ allows you to check for the character values. In this case, using characters rather than numbers makes the code much more readable.
After the switch
, add the final code to print out the result:
cout << endl; cout << arg1 << " " << op << " " << arg2; cout << " = " << result << endl;
You can now compile the code and test it with calculations that involve +
, -
, and *
.
Division is a problem, because it is invalid to divide by zero. To test this out, add the following lines to the bottom of the switch
:
case '/': result = arg1 / arg2; break;
Compile and run the code, passing zero as the final parameter:
C:\Beginning_C++Chapter_02>calc 1 / 0 1 / 0 = inf
The code ran successfully, and printed out the expression, but it says that the result is an odd value of inf
. What is happening here?
The division by zero assigned result
to a value of NAN
, which is a constant defined in <math.h>
(included via <cmath>
), and means "not a number." The double
overload of the insertion operator for the cout
object tests to see if the number has a valid value, and if the number has a value of NAN
, it prints the string inf
. In our application, we can test for a zero divisor, and we treat the user action of passing a zero as being an error. Thus, change the code so that it reads as follows:
case '/': if (arg2 == 0) { cout << endl << "divide by zero!" << endl; return 1; } else { result = arg1 / arg2; } break;
Now when the user passes zero as a divisor, you will get a divide by zero!
message.
You can now compile the full example and test it out. The application supports floating-point arithmetic using the +
, -
, *
, and /
operators, and will handle the case of dividing by zero.