Skip to main content

The following C++ programming details will be helpful in completing the initial homeworks for the course.

Command Line Arguments

Most of the programs we write will expect additional information from the user when the program is launched. We will do this by providing this data on the command line:

  ./my_program.exe blue 3.14159 my_input_data_file.txt my_output_data_file.txt

In order for your program to receive arguments from the command line, you will make use of the optional arguments to the main function. Here is the prototype:

  int main(int argc, char* argv[])

The parameter argc contains the number of strings on the command line, including the executable name. For the example above, argc = 5. The array argv stores those C-style strings. You can access the executable name with argv[0] and the arguments with argv[1], argv[2], etc.

Important Note: To compare command line argument strings using the == operator, you must cast this data to a STL C++-style string:

  if (std::string(argv[1]) == std::string("blue")) {
     std::cout << "my favorite color is blue too!" << std::endl;
  }

Command Line Arguments in Visual Studio

You can also specify command line arguments when running the program inside of the Visual Studio development environment. Go to "Project" → "Properties" → "Configuration Properties" → "Debugging" and in "Command arguments" enter:

  blue 3.14159 my_input_data_file.txt my_output_data_file.txt

Note, that the default directory for files is the working directory.

Converting a C/C++ String to an Integer or Floating Point Value

If you have a C++ string that contains only digits '0'-'9', you can convert the string to an integer using the STL stoi function from the #include <string> header file. Note: You need to use a compiler supporting C++11 (or greater).

  std::string my_int_string = "17";
  int x = std::stoi(my_int_string);

And similarly you can convert a C++ string with digits and a single period/decimal point into a float:

  std::string my_float_string = "3.14159";
  float y = std::stof(my_float_string);

If your variable is a C-style char array, char*, you should cast the C-style char* to a C++ STL string before using the functions above. Or you can use the C versions of these functions atoi (char-to-integer) and atof (char-to-float) from the #include <cstdlib> header file.

Converting an integer or floating point number to a C++ String

The STL output stream (std::cout or std::ofstream) will automatically convert integers and floating point numbers to string representation for printing:

  int x = 17;
  float y = 3.14159;
  std::cout << "my output is " << x << " and " << y << std::endl;

But we can also convert these number types to C++ string objects:

  std::string x_string = std::to_string(x);
  std::string y_string = std::to_string(y);

Giving us a preview of how they would print to the stream, and allowing us to check the length of the printed string or do other operations on the string object.

Reading From & Writing To Files

The STL streams std::cin & std::cout are used to read data from and write data to the "console". Often, we would rather read data from a file and/or write the output to a file. We can do this using the STL file stream library:

  #include <fstream>

Here is an example fragment of code that attempts to open an input file stream for a file name specified on the command line above:

  std::ifstream in_str(argv[3]);

It is good coding practice to verify that the input stream was successfully opened:

  if (!in_str.good()) {
    std::cerr << "Can't open " << argv[3] << " to read.\n";
    exit(1);
  }

Likewise here's how to open a stream for output:

  std::ofstream out_str(argv[4]);
  if (!out_str.good()) {
    std::cerr << "Can't open " << argv[4] << " to write.\n";
    exit(1);
  }

Once the streams are created, you can use in_str & out_str just like you use std::cin & std::cout.

In general, for this course, we encourage you to use the stream operator>> for all input parsing, rather than using getline, eof, getc, etc. We will not deduct points for using the other methods of parsing, but we have designed the assignment input format specifications for easy parsing with >>.

Note that the following code has a bug. If the input file ends with one or more extra newlines, the inner loop will "do something" with the last successfully read element twice. It is important to check the return value of each >> expression to be sure the read was successful.

  while (!in_str.eof()) {
    in_str >> my_variable;
    // do something with my_variable
  }

A simpler and more robust way to write the same code is:

  while (in_str >> my_variable) {
    // do something with my_variable
  }

File Parsing Example with Different Data Types

For example, if your input file contains family last name, number of children, and ages of the children:

  Smith   3   4.5   6.0   8.1
  Jones   1  13.6
  Lee     2   1.5   4.2

Here is code to read all of this data:

  std::string last_name;
  int num_children;
  std::vector<float> ages;
  float tmp;
  while (in_str >> last_name) {
    in_str >> num_children;
    // error checking to make sure num_children is not negative
    assert (num_children >= 0);				  
    // clear out ages data from the last family
    ages.clear();  
    for (int i = 0; i < num_children; i++) {
       in_str >> tmp;
       ages.push_back(tmp);
    }				  
    // Do something interesting with the last_name and ages variables!
  }

Note that we do not use getline or attempt to find a newline character, so we are not relying on the newlines or spaces or tabs being in specific places in the file. We only assume that data is consistent -- the number of children is non-negative and the number of floats matches the specified number of children. We could do more error checking on each >> operation to make sure each read did not fail, but most of the time in this course its ok to assume the input won't be terribly broken. If there is an error in the input, this code will get confused and break or crash. Robustly handling all broken input is beyond the scope of this course.

Random Numbers

Here's a sample program that uses the STL's srand and rand functions to generate and print random positive integers:

  #include <iostream>
  #include <cstdlib>
  #include <ctime>
  
  int main() {
    // "seed" the random number generate with the current time (in seconds)
    std::srand(std::time(0));
    // print the value of RAND_MAX (system dependent)
    std::cout << "RAND_MAX is " << RAND_MAX << std::endl;
    for (int i = 0; i < 10; i++) {
      // generate a number between [ 0, RAND_MAX ]
      int a = std::rand();
      // use modular arithmetic to convert the number to the range [ 0, 20 )
      int b = a % 20;
      std::cout << a << " " << b << std::endl;
    }
  }

Generally you should only call srand once at the start of the program. A common trick is to seed the number generator with the current time. Two runs of the program at different times (at least 1 second apart) will have different sequence of values for subsequent calls to rand. Here is sample output of the above program:


  RAND_MAX is 2147483647
  1437138704 4
  1241620319 19
  814103534 14
  1019780901 1
  390616400 0
  232325921 1
  576484001 1
  1667873190 10
  840660039 19
  678361860 0

This example is sufficient for simple usage of random numbers. Some applications (e.g., cryptography, scientific simulation, statistics, quantum research, etc.) require more specialized methods of generating random numbers with specific properties (e.g, larger range, decimals, an unbiased distribution, and/or faster performance).

Comparing Two Text Files

To check the correctness of your program, you can compare your output text file to the provided sample output text file using the UNIX utility diff (available on Linux & WSL & MacOS):

  diff my_output.txt sample_output.txt

Any lines in the two files that are not identical (including whitespace), will be printed to the console. WinDiff is another option for Windows users. Please see a TA or the instructor in office hours if you have a question about these programs.

Redirecting Input & Output

What if you have an interactive program that uses std::cin & std::cout to read from and write to the "console", but you'd like to take the input from a file and you'd rather not rewrite the program to use the input & output streams described above? This following trick is handy for repeated testing and debugging an interactive program (you'd rather not have to manually type in the same input test data many times). Asking the executable to read from a file instead of the console and/or write to a file instead of the console is called I/O redirection.

  • First, create the input.txt file that contains input which you would otherwise type at the console during program's execution.

  • Then on the Linux/WSL/MacOS command prompt simply type:

  •    program.exe < input.txt > output.txt
    

  • When the program has finished, look in the newly created file output.txt.

  • NOTE: If your program is printing to both std::cout and std::cerr run your program like this to separately capture these streams:

  •    program.exe < input.txt > output.txt 2> cerr_output.txt
    

    This is how we run all automated testing of student programs on Submitty!

You can do the same thing in Visual Studio. (See section on "Command Line Arguments in Visual Studio" above).