Book Image

Modern C++: Efficient and Scalable Application Development

By : Richard Grimes, Marius Bancila
Book Image

Modern C++: Efficient and Scalable Application Development

By: Richard Grimes, Marius Bancila

Overview of this book

C++ is one of the most widely used programming languages. It is fast, flexible, and used to solve many programming problems. This Learning Path gives you an in-depth and hands-on experience of working with C++, using the latest recipes and understanding most recent developments. You will explore C++ programming constructs by learning about language structures, functions, and classes, which will help you identify the execution flow through code. You will also understand the importance of the C++ standard library as well as memory allocation for writing better and faster programs. Modern C++: Efficient and Scalable Application Development deals with the challenges faced with advanced C++ programming. You will work through advanced topics such as multithreading, networking, concurrency, lambda expressions, and many more recipes. By the end of this Learning Path, you will have all the skills to become a master C++ programmer. This Learning Path includes content from the following Packt products: • Beginning C++ Programming by Richard Grimes • Modern C++ Programming Cookbook by Marius Bancila • The Modern C++ Challenge by Marius Bancila
Table of Contents (24 chapters)
Title Page
Copyright
About Packt
Contributors
Preface
12
Math Problems
13
Language Features
14
Strings and Regular Expressions
15
Streams and Filesystems
16
Date and Time
17
Algorithms and Data Structures
Index

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

+

42

*

43

-

45

/

47

 

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.