C++ and Catch – Adding your Own Main Method

When you begin coding on a project, it is perfectly acceptable and even advisable to allow the Catch library to generate the main method for you.  That is what the #define CATCH_CONFIG_MAIN (very first line in the tests.cpp file)  directive tells Catch to do.

As you transition from implementing the data structures to implementing a higher-level project, you will want to eventually create your own main method.  Here is how to transition to using your own main without getting rid of tests and testing.

In QtCreator, follow these steps

  1. Add a new cpp file to your project that will contain your main driver.  If you still have the original main.cpp that was added when you created the project, that is fine to use as well; make sure it is listed in the project explorer on the left side of the code window.
  2. Comment out#define CATCH_CONFIG_MAIN at the top of the tests.cpp file.  This will tell the Catch library NOT to generate its own main method.
  3. In your main driver file, copy and paste the following code (to start with). Read the comments throughout to help you understand what is going on.
//CATCH_CONFIG_RUNNER tells the catch library that this 
//project will now explicitly call for the tests to be run. 
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

//A macro used in main to determine if you want to run
//the tests or not. If you don't want to run your tests,
//change true to false in the line below.
#define TEST true

/*
* runCatchTests will cause Catch to go ahead and
* run your tests (that are contained in the tests.cpp file.
* to do that, it needs access to the command line
* args - argc and argv. It returns an integer that
* ultimately gets passed back up to the operating system.
* See the if statement at the top of main for
* a better overview.
*/
int runCatchTests(int argc, char* const argv[])
{
    //This line of code causes the Catch library to 
    //run the tests in the project. 
    return Catch::Session().run(argc, argv);
}

int main( int argc, char* const argv[] )
{
    //If the TEST macro is defined to be true,
    //runCatchTests will be called and immediately
    //return causing the program to terminate. Change TEST
    //to false in the macro def at the top of this file
    //to skip tests and run the rest of your code.
    if (TEST)
    {
        return runCatchTests(argc, argv);
    }

    //start working on other parts of your project here.
    return 0;
}

Once you’ve added that code, rebuild your project (Build menu| Rebuild All) then execute your project.  Your tests should run as normal.

Let’s Review Pointers

Pointers cause a lot of heartburn among students.  Hopefully this post will address some or all of the things you may be struggling with in the world of pointers.

For pointers to make sense, particularly the parts of them that are important for this class, you need to remember a few fundamental pieces of information:

  • Each location where data can be stored has an address.
    • Analogy: Every post office box in Hughes Trigg has an individual address.  If each didn’t, then the workers wouldn’t know what mail goes in which box.
    • Note that even pointers have addresses.  So, if I have [Read more…]

Memory Diagrams

The fact that c++ allows programmers to manage memory directly is one of its great strengths. It is also something that can lead to a ton of tough debugging and hair-pulling.  There are quite a few “things” a programmer can use to avoid most of these problems, but it is important to understand what’s going on under the hood of C++ so that you can fully understand why those other libraries are useful.

Memory Diagrams can help you not only learn the ins and outs of memory management, but they can also help in a debugging situation as well.  A memory diagram is a drawing that represents the state of the memory used by a program at a particular point in execution.  Of course, it is an abstraction of the actual memory usage, but contains enough detail to be very useful.

A memory diagram usually contains two major sections: 1) stack memory, and 2) heap memory.  These two are usually split between the left and the right on a piece of paper. Here is a template you can have a look at:  Memory Diagram Template.

Here is a link to a screencast on Vimeo I made a couple semesters ago related to drawing memory diagrams.  We’ll also be going over them in class as we talk about memory management.

Some related help:

  • Eric Roberts’ (Stanford) Heap-Stack DiagramsHandout on Heap-Stack Diagrams from their Lab sections (This is a link to some of the PDFs saved in Evernote) – These diagrams are quite a bit more “low level” than the method I use, but the general idea is the same.  The memory diagrams start at Problem 3.  A pdf of the solutions is included as well.
  • Debugging Software Crashes II – quite detailed, but a good resource for understanding memory.

Objects and Pointers

Some of the previous videos and reading have covered the implications of an object owning some dynamically allocated memory.  Mainly, what I’m referring to here is the need for a copy constructor, overloaded assignment operator, and destructor in a class that contains any dynamically allocated memory.  That’s all important stuff.  Know it, learn it, love it.

But what about the dynamic allocation of objects themselves – how does it work and what are the implications.  I’m glad you asked. Dynamically allocating an object is just like dynamically allocating anything else.   [Read more…]

Object-Oriented Programming in C++

C++ was originally called “c with classes” meaning that it was everything that the c language was plus the ability to create classes and objects in the language.  I’m sure that you can see the value in creating a higher level construct over and above a simple variable.  For example, if we were modeling the data for a group of workers at a company, we would want to keep track of a number of different things about each employee: name, address, employee id number, yearly salary, and department perhaps.  We could make use of the concept of parallel arrays meaning that we have 2 or more arrays whose elements are related via their position (or subscript) in the array.  So, name[9], empID[9], salary[9], etc would all refer to data about the same employee because each is being accessed with the same subscript.  However, after a while, this becomes cumbersome and isn’t very extensible as we add new variables to track.  [Read more…]

Arrays of c-strings

There are a lot of use cases in which you deal with a set of strings.  An array of strings seems to be a logical, first-pass choice for storing them.  Because c-strings are basic arrays fundamentally, they are a great topic to use to understand the array/pointer relationship more fully.

Consider, the following array declaration:

char data[5][10] = {"red", "yellow", "green", "blue", "violet"};

Because data is the name of a 2D array, it can be treated as a pointer to a pointer, or more precisely in this case, a pointer to an array of pointers.  Because we are dealing with a 2D array of chars with each row acting as a null-terminated c-string, then we can also conceptualize data as an array of c-strings.  So, if I have an array of c-strings, then I can display just one c-string from the array by sending the address of the 1st element of the array to cout like this:

cout << data[1] << endl; //would print yellow

Remember, if you send an ostream object a char pointer, it will start printing at that character until it gets to a null terminator.  This means that if we send it the address of the first letter of a row and an additional offset (but not subscripted), it will start printing at that letter and go to the null terminator as well.  Here is an example:

cout << data[2] + 2 << endl; //would print een of green

The expression data[2]+2 is the memory address of the 3rd character in the 3rd string of the array.  Therefore, it will start printing at that character and print until the end of the string.

You may be asking yourself, “What’s the difference between the example above and data[2][2]?”  Great question!  The difference is that in data[2][2], the 2nd number is subscripted, which means essentially that it is dereferenced.  So, the data type of the expression data[2]+2 is char* while the data type of the expression data[2][2] is char.  If we send cout a char, it will only print that one character.

Pointer Offset Notation and Subscript Notation

As we know,

data[2]

is equivalent to

*(data + 2)

One first is subscript notation, then second is pointer-offset notation.  We can include multiple levels of offsets depending on the data type of the base constructs (the datatype of data, in this case).  So, this means that the expression

data[2][2]

is equivalent to the expression

*(*(data + 2)+2)

If you think about starting at the deepest level of nesting and work your way out, you’ll see that they are equivalent.

As an example, the following two statements produce the same output:

cout << (data[1] + 3) << endl;
cout << (*(data+1)+3) << endl;

The relationship between pointer-offset notation and subscript notation can seem to be a little confusing.  But if you just take it slow, it all makes logical sense.

Peace, love, c++!

Memory Management in C++

C++ gives you, the programmer, more fine-grained access to memory than a language like Java.  An old saying goes like this, “To whom much is give, much is expected.”  So, because you have more control over memory, you have to take care to handle it appropriately.  This means that if you dynamically allocate memory, you should de-allocate it when you’re done with it.

Here are a few YouTube videos that are similar to stuff I’ve talked about.  But hearing the info explained a little differently might be helpful.

Basic Dynamic Allocation and Deallocation:  Video

Memory Leaks and Dangling Pointers: Video (The writing is a little bit hard to read, but it is a good explanation)

Pointers to Pointers: Video (Same issue as above, but good expl).

It’s Not Nice to Point… But Really, It’s OK.

Pointers are really important to the C and C++ language.  They are actually really important in many different languages whether or not you have direct access to manipulate them. In our on-line gathering yesterday evening, I introduced you to the basic concepts of pointers and memory layout, well – at least how C++ sees it.

It’s time to read Section 6.3 of Overland (skip 6.3.7, 6.3.8 and 6.3.9). Also read Section 6.4, but stop before the paragraph that starts with “This analysis—what would the item imply…”.  From that point down is about function pointers which should be in its own sub section.  But in any event, you don’t need to worry about these. 

Here are a couple videos that might also be useful to you that I made a few semesters ago: Video 1, Video 2.

Reading sections 10.1, 10.2, and 10.3 of Overland related to c-strings could be helpful if you need more info there.

2 Dimensions are Better than 1, right?

Many problems deal with more than single dimensional data.  At the risk of beating the grade example to pieces, think about the idea of a class having 20 students and 4 exam grades for a semester.  Would it be possible to model this scenario with a 1D array?  Sure, absolutely.  Would it be fun?  Very unlikely.  Another example that you work with every day is graphics. [Read more…]

Arrays – A Ray of Sunshine for your Programs

Arrays are a foundational data structure in many programming languages, especially those that come from the same genre as c++.  However, different languages treat arrays different, meaning that they have different “features”. [Read more…]