Learn C++

May 28th, 2008

Server configuration completed

Hello everyone,

The server configuration has been largely completed and the problem that was causing the site to crash every few hours has been found and remedied. The forums are now online again — so we should be up and running now faster than ever.

Thanks for bearing through all this maintenance stuff over the past month.

If you encounter any problems or find stuff that isn’t working, please post a response to this thread.

Thanks,
Alex

May 9th, 2008

Disabled features

While installing the caching plugin was successful, our consumed resources did not drop near as much as anticipated for some reason. Consequently, I am going to be disabling various plugins to try and see if there is one or two that is causing a huge amount of CPU time to be eaten up. This process will take place over the next week. The core site content should be available, but some things, like the ability to edit posts, may be disabled for a while. Please bear with us through this process.

Thanks,

Alex

May 6th, 2008

Site slowness / caching plugin installation warning

Hey everyone,

Enough of you have been visiting this site that our host has become unhappy with the resource utilization we’re causing on the server this site is hosted on. Consequently, they’ve moved this site to another server that is likely to have less performance until I can get a handle on the resource utilization. The end result is that the site will be slower for a period of time until I can remedy the issue.

The plan is that I will be installing a caching plugin in order to reduce the number of PHP and SQL queries the web site makes on the server. When this takes place, it is quite likely that some features of the site will break, or not update correctly. If you notice this happening, please leave a comment on this thread and let me know and I will try to fix it.

My apologies for the interruption, but it’s either fix it or take the site down… I’m going to go with fix it. :)

Alex

Edit: The cache is installed and active. Let me know if you notice any problems. Thanks!

April 29th, 2008

14.2 — Function template instances

Function template instances

It’s worth taking a brief look at how template functions are implemented in C++, because future lessons will build off of some of these concepts. It turns out that C++ does not compile the template function directly. Instead, at compile time, when the compiler encounters a call to a template function, it replicates the template function and replaces the template type parameters with actual types! The function with actual types is called a function template instance.

Let’s take a look at an example of this process. First, we have a templated function:


template <typename Type> // this is the template parameter declaration
Type max(Type tX, Type tY)
{
    return (tX > tY) ? tX : tY;
}

When compiling your program, the compiler encounters a call to the templated function:


int nValue = max(3, 7); // calls max(int, int)

The compiler says, “oh, we want to call max(int, int)”. The compiler replicates the function template and creates the template instance max(int, int):


int max(int tX, int tY)
{
    return (tX > tY) ? tX : tY;
}

This is now a “normal function” that can be compiled into machine language.

Now, let’s say later in your code, you called max() again using a different type:


double dValue = max(6.34, 18.523); // calls max(double, double)

C++ automatically creates a template instance for max(double, double):


double max(double tX, double tY)
{
    return (tX > tY) ? tX : tY;
}

and then compiles it into machine language.

It’s worth noting that the compiler is smart enough to know it only needs to create one template instance per set of unique type parameters. It’s also worth noting that if you create a template function but do not call it, no template instances will be created.

Operators, function calls, and function templates

Template functions will work with both built-in types (eg. char, int, double, etc…) and classes, with one caveat. When the compiler compiles the template instance, it compiles it just like a normal function. In a normal function, any operators or function calls that you use with your types must be defined, or you will get a compiler error. Similarly, any operators or function calls in your template function must be defined for any types the function template is instantiated for. Let’s take a look at this in more detail.

First, we’ll create a simple class:


class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents)
        : m_nCents(nCents)
    {
    }
};

Now, let’s see what happens when we try to call our templated max() function with the Cents class:


Cents cNickle(5);
Cents cDime(10);

Cents cBigger = max(cNickle, cDime);

C++ will create a template instance for max() that looks like this:


Cents max(Cents tX, Cents tY)
{
    return (tX > tY) ? tX : tY;
}

And then it will try to compile this function. See the problem here? C++ has no idea how to evaluate tX > tY! Consequently, this will produce a compile error.

To get around this problem, simply overload the > operator for any class we wish to use max() with:


class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents)
        : m_nCents(nCents)
    {
    }

    friend bool operator>(Cents &c1, Cents&c2)
    {
        return (c1.m_nCents > c2.m_nCents) ? true : false;
    }
};

Now C++ will know how to compare tX > tY when tX and tY are objects of the Cents class! As a result, our max() function will now work with two objects of type Cents.

Another example

Let’s do one more example of a function template. The following function template will calculate the average of a number of objects in an array:


template <class T>
T Average(T *atArray, int nNumValues)
{
    T tSum = 0;
    for (int nCount=0; nCount < nNumValues; nCount++)
        tSum += atArray[nCount];

    tSum /= nNumValues;
    return tSum;
}

Now let’s see it in action:


int anArray[] = { 5, 3, 2, 1, 4 };
cout << Average(anArray, 5) << endl;

double dnArray[] = { 3.12, 3.45, 9.23, 6.34 };
cout << Average(dnArray, 4) << endl;

This produces the values:

3
5.535

As you can see, it works great for built-in types!

Now let’s see what happens when we call this function on our Cents class:


Cents cArray[] = { Cents(5), Cents(10), Cents(15), Cents(14) };
cout << Average(cArray, 4) << endl;

The compiler goes berserk and produces a ton of error messages! The first error message will be something like this:

c:\test.cpp(45) : error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'Cents' (or there is no acceptable conversion)

Remember that Average() returns a Cents object, and we are trying to stream that object to cout using the << operator. However, we haven't defined the << operator for our Cents class yet. Let's do that:


class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents)
        : m_nCents(nCents)
    {
    }

    friend ostream& operator<< (ostream &out, Cents &cCents)
    {
        out << cCents.m_nCents << " cents ";
        return out;
    }
};

If we compile again, we will get another error:

c:\test.cpp(14) : error C2676: binary '+=' : 'Cents' does not define this operator or a conversion to a type acceptable to the predefined operator

This error is actually being caused by the function template instance created when we call Average(Cents*, int). Remember that when we call a templated function, the compiler “stencils” out a copy of the function where the template type parameters (the placeholder types) have been replaced with the actual types in the function call. Here is the function template instance for Average() when T is a Cents object:


template <class T>
Cents Average(Cents  *atArray, int nNumValues)
{
    Cents tSum = 0;
    for (int nCount=0; nCount < nNumValues; nCount++)
        tSum += atArray[nCount];

    tSum /= nNumValues;
    return tSum;
}

The reason we are getting an error message is because of the following line:


        tSum += atArray[nCount];

In this case, tSum is a Cents object, but we have not defined the += operator for Cents objects! We will need to define this function in order for Average() to be able to work with Cents. Looking forward, we can see that Average() also uses the /= operator, so we will go ahead and define that as well:


class Cents
{
private:
    int m_nCents;
public:
    Cents(int nCents)
        : m_nCents(nCents)
    {
    }

    friend ostream& operator<< (ostream &out, Cents &cCents)
    {
        out << cCents.m_nCents << " cents ";
        return out;
    }

    void operator+=(Cents cCents)
    {
        m_nCents += cCents.m_nCents;
    }

    void operator/=(int nValue)
    {
        m_nCents /= nValue;
    }
};

Finally, our code will compile and run! Here is the result:

14 cents

Note that we didn’t have to modify Average() at all to make it work with objects of type Cents. We simply had to define the operators used to implement Average() for the Cents class, and the compiler took care of the rest!

14.3 — Template classes
Index
14.1 — Function templates
April 18th, 2008

14.1 — Function templates

The need for function templates

In previous chapters, you’ve learned how to write functions and classes that help make programs easier to write, safer, and more maintainable. While functions and classes are powerful and flexible tools for effective programming, in certain cases they can also be somewhat limiting because of C++’s requirement that you specify the type of all parameters.

For example, let’s say you wanted to write a function to calculate the maximum of two numbers. You might do so like this:


int max(int nX, int nY)
{
    return (nX > nY) ? nX : nY;
}

This function would work great — for integers. What happens later when you realize your max function needs to work with doubles? Traditionally, the answer would be to overload the max() function and create a new version that works with doubles:


double max(double dX, double dY)
{
    return (dX > dY) ? dX : dY;
}

Note that the code for the implementation of the double version of max() is exactly the same as for the int version of max()! In fact, this implementation would work for all sorts of different types: chars, ints, doubles, and if you’ve overloaded the > operator, even classes! However, because C++ requires you to make your variables specific types, you’re stuck writing one function for each type you wish to use.

Having to specify different “flavors” of the same function where the only thing that changes is the type of the parameters can become a severe maintenance headache and time-waster, and it also violates the general programming guideline that duplicate code should be minimized as much as possible. Wouldn’t it be nice if we could write one version of max() that was able to work with parameters of ANY type?

This is where function templates come in!

What is a function template?

If you were to look up the word “template” in the dictionary, you’d find a definition that was similar to the following: “a template is a model that serves as a pattern for creating similar objects”. One type of template that is very easy to understand is that of a stencil. A stencil is an object (eg. a piece of cardboard) with a shape cut out of it (eg. the letter J). By placing the stencil on top of another object, then spraying paint through the hole, you can very quickly produce stenciled patterns in many different colors! Note that you only need to create a given stencil once — you can then use it as many times as you like to create stenciled patterns in whatever color(s) you like. Even better, you don’t have to decide the color of the stenciled pattern you want to create until you decide to actually use the stencil.

In C++, function templates are functions that serve as a pattern for creating other similar functions. The basic idea behind function templates is to create a function without having to specify the exact type(s) of some or all of the variables. Instead, we define the function using placeholder types, called template type parameters. Once we have created a function using these placeholder types, we have effectively created a “function stencil”.

It turns out that you can’t call a function template directly — this is because the compiler doesn’t know how to handle placeholder types directly. Instead, when you call a template function, the compiler “stencils” out a copy of the template, replacing the placeholder types with the actual variable types in your function call! Using this methodology, the compiler can create multiple “flavors” of a function from one template! We’ll take a look at this process in more detail in the next lesson.

Creating function templates in C++

At this point, you’re probably wondering how to actually create function templates in C++. It turns out, it’s not all that difficult.

Let’s take a look at the int version of max() again:


int max(int nX, int nY)
{
    return (nX > nY) ? nX : nY;
}

Note that there are 3 places where specific types are used: parameters nX, nY, and the return value all specify that they must be integers. To create a function template, we’re going to replace these specific types with placeholder types. In this case, because we have only one type that needs replacing (int), we only need one template type parameter. Let’s call our this placeholder type “Type”. You can name your placeholder types almost anything you want, so long as it’s not a reserved word. Here’s our new function with a placeholder type:


Type max(Type tX, Type tY)
{
    return (tX > tY) ? tX : tY;
}

(Note: I also changed the Hungarian notation on the variables to reflect that they are not necessarily integers any longer — they are whatever type Type is!)

This is a good start — however, it won’t compile because the compiler doesn’t know what “Type” means! In order to tell the compiler that Type is meant to be a placeholder type, we need to formally tell the compiler that Type is a template type parameter. This is done using what is called a template parameter declaration:


template <typename Type> // this is the template parameter declaration
Type max(Type tX, Type tY)
{
    return (tX > tY) ? tX : tY;
}

Believe it or not, we’re done! This will compile!

Now, let’s take a slightly closer look at the template parameter declaration. We start with the keyword template — this tells the compiler that what follows is going to be a list of template parameters. We place all of our parameters inside angled brackets (<>). To create a template type parameter, use either the keyword typename or class. There is no difference between the two keywords in this context, and you will usually see people use the class keyword. However, we prefer the newer typename keyword, because it makes it clearer that the template type parameter doesn’t have to be a class. After the typename or class keyword, all that’s left is to pick a name for your placeholder type. Traditionally, with function that have only one template type parameter, the name “Type” (often shortened to “T”) is used. If the template function uses multiple template type parameter, they can be separated by commas:


template <typename T1, typename T2>
// template function here

Using function templates

Using a function template is extremely straightforward — you can use it just like any other function:


int nValue = max(3, 7); // returns 7
double dValue = max(6.34, 18.523); // returns 18.523
char chValue = max('a', '6'); // returns 'a'

Note that all three of these calls to max() have parameters of different types!

As you can see, template functions can save a lot of time, because you only need to write one function, and it will work with many different types. Once you get used to writing function templates, you’ll find they actually don’t take any longer to write than functions with actual types. Template functions reduce code maintenance, because duplicate code is reduced significantly. And finally, template functions can be safer, because there is no need to copy functions and change types by hand whenever you need the function to work with a new type!

Template functions do have a few drawbacks, and we would be remiss not to mention them. First, older compilers generally do not have very good template support. However, modern compilers are much better at supporting and implementing template functionality properly. Second, template functions produce crazy-looking error messages that are much harder to decipher than those of regular functions. However, these drawbacks are fairly minor compared with the power and flexibility templates bring to your programming toolkit!

14.2 — Function template instances
Index
13.7 — Random file I/O