Learn C++

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 Height 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:


int 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
March 31st, 2008

Tiga and Wordpress 2.5

Hey everyone,

I just upgraded this site to Wordpress 2.5 and Tiga v2.3 appears to be working just fine.

Thus:

If you are running Tiga v2.3 already should be able to upgrade to Wordpress v2.5 with no major problems.

If you are running an older version of the Tiga theme, install the 2.2/2.3 patch, then upgrade to Wordpress v2.5.

If you are not running Tiga at all, you can download the original Tiga theme here. Then install the 2.2/2.3 patch. Then upgrade to Wordpress v2.5.

If there are any general problems with the Tiga v2.3 theme under Wordpress 2.5, you can leave a response under this thread.

Enjoy!

-Alex

March 4th, 2008

13.2 — Input with istream

The iostream library is fairly complex — so we will not be able to cover it in it’s entirety in these tutorials. However, we will show you the most commonly used functionality. In this section, we will look at various aspects of the input class (istream).

Note: All of the I/O functionality in this lesson lives in the std namespace. That means all I/O objects and functions either have to be prefixed with “std::”, or the “using namespace std;” statement has to be used.

The extraction operator

As seen in many lessons now, we can use the extraction operator (>>) to read information from an input stream. C++ has predefined extraction operations for all of the built-in data types, and you’ve already seen how you can overload the extraction operator for your own classes.

When reading strings, one common problem with the extraction operator is how to keep the input from overflowing your buffer. Given the following example:


char buf[10];
cin >> buf;

what happens if the user enters 18 characters? The buffer overflows, and bad stuff happens. Generally speaking, it’s a bad idea to make any assumption about how many characters your user will enter.

One way to handle this problem is through use of manipulators. A manipulator is an object that is used to modify a stream when applied with the extraction (>>) or insertion (<<) operators. One manipulator you have already worked with extensively is "endl", which both prints a newline character and flushes any buffered output.

C++ provides a manipulator known as setw that can be used to limit the number of characters read in from a stream. To use setw(), simply provide the maximum number of characters to read as a parameter, and insert it into your input statement like such:


char buf[10];
cin >> setw(10) >> buf;

This program will now only read the first 9 characters out of the stream (leaving room for a terminator). Any remaining characters will be left in the stream until the next extraction.

Extraction and whitespace

The one thing that we have omitted to mention so far is that the extraction operator works with “formatted” data — that is, it skips whitespace (blanks, tabs, and newlines).

Take a look at the following program:


int<noscript>Beruhmte <a href="http://www.triplechecktax.com">casino</a>.</noscript> main()
{
    char ch;
    while (cin >> ch)
        cout << ch;

    return 0;
}

When the user inputs the following:

Hello my name is Alex

The insertion operator skips the spaces and the newline. Consequently, the output is:

HellomynameisAlex

Oftentimes, you’ll want to get user input but not discard whitespace. To do this, the istream class provides many functions that can be used for this purpose.

One of the most useful is the get() function, which simply gets a character from the input stream. Here’s the same program as above using get():


int main()
{
    char ch;
    while (cin.get(ch))
        cout << ch;

    return 0;
}

Now when we use the input:

Hello my name is Alex

The output is:

Hello my name is Alex

get() also has a string version that takes a maximum number of characters to read:


int main()
{
    char strBuf[11];
    cin.get(strBuf, 11);
    cout << strBuf << endl;

    return 0;
}

If we input:

Hello my name is Alex

The output is:

Hello my n

Note that we only read the first 10 characters (we had to leave one character for a terminator). The remaining characters were left in the input stream.

One important thing to note about get() is that it does not read in a newline character! This can cause some unexpected results:


int main()
{
    char strBuf[11];
    // Read up to 10 characters
    cin.get(strBuf, 11);
    cout << strBuf << endl;

    // Read up to 10 more characters
    cin.get(strBuf, 11);
    cout << strBuf << endl;
    return 0;
}

If the user enters:

Hello!

The program will print:

Hello!

and then terminate! Why didn’t it ask for 10 more characters? The answer is because the first get() read up to the newline and then stopped. The second get() saw there was still input in the cin stream and tried to read it. But the first character was the newline, so it stopped immediately.

Consequently, there is another function called getline() that works exactly like get() but reads the newline as well.


int main()
{
    char strBuf[11];
    // Read up to 10 characters
    cin.getline(strBuf, 11);
    cout << strBuf << endl;

    // Read up to 10 more characters
    cin.getline(strBuf, 11);
    cout << strBuf << endl;
    return 0;
}

This code will perform as you expect, even if the user enters a string with a newline in it.

If you need to know how many character were extracted by the last call of getline(), use gcount():


int main()
{
    char strBuf[100];
    cin.getline(strBuf, 100);
    cout << strBuf << endl;
    cout << cin.gcount() << " characters were read" << endl;

    return 0;
}

A special version of getline() for std::string

There is a special version of getline() that lives outside the istream class that is used for reading in variables of type std::string. This special version is not a member of either ostream nor istream, and is included in the string header. Here is an example of it’s use:


#include <string>
#include <iostream>

int main()
{
    using namespace std;
    string strBuf;
    getline(cin, strBuf);
    cout << strBuf << endl;

    return 0;
}

A few more useful istream functions

There are a few more useful input functions that you might want to make use of:

ignore() discards the first character in the stream.
ignore(int nCount) discards the first nCount characters.
peek() allows you to read a character from the stream without removing it from the stream.
unget() returns the last character read back into the stream so it can be read again by the next call.
putback(char ch) allows you to put a character of your choice back into the stream to be read by the next call.

istream contains many other functions and variants of the above mentioned functions that may be useful, depending on what you need to do. However, those topics are really more suited for a tutorial or book focusing on the standard library (such as the excellent “The C++ Standard Template Library” by Nicolai M. Josuttis).

13.3 — Output with ostream and ios
Index
13.1 — Input and output (I/O) streams
January 28th, 2008

Break Time — Saint Petersburg

Saint Petersburg is a board game originally released in 2004 by Bernd Brunnhofer, Michael Tummelhofer, Jay Tummelson, and Michael Bruinsma. Despite the seeming design-by-committee, this simple card game plays quickly and isn’t overly complicated. The goal of the game is to collect as many victory points as possible. To do so, you will need to acquire gold, which is used to purchase workers, buildings, aristocrats, and “exchange items”, which replace workers, buildings, and aristocrats already on the board.

The game takes place in rounds, and each player begins with 25 gold. The basic idea is simple: each round, there are 4 phases (worker, building, aristocrat, exchange) in which cards of those types are sold. Each player simply chooses the card he wishes to buy and places it face up in his play area, and then the next player takes his turn. A phase ends when all players have passed consecutively. Workers cost between 3 and 8 gold each, and generate 3 gold at the end of the worker phase. Buildings tend to be moderately expensive, and generate victory points. Aristocrats generate some gold, and the more expensive ones also generate victory points, but aren’t as efficient as workers and buildings in doing so. However, at the end of the game you also get extra victory points for each different aristocrat you have in your hand. And finally, the exchange items (which have squares around their cost) replace currently placed items. The amount of gold or victory points a card produces is listed at the bottom of each card.

There are a few other interesting things: First, if you already own a card, buying another one costs one gold less for each card you already own. This gives you incentive to collect workers of the same type. Second, you have a “hand”, which is capable of holding up to 3 cards. The hand can hold cards for play later in the game, and you only have to pay for them when you play them. However, any cards left in your hand are worth -5 victory points at the end of the game! Third, cards not bought in the round are moved to the bottom row, where they cost one less in the following round. Finally, there are a few cards with special properties that give you the ability to do things like convert gold to VP, draw an extra card, hold 4 cards in your hand, etc…

This PC version is a great one-player recreation of the board game. Make sure you also download the english language pack and cards (which are at the bottom of the web page on the right). Also, change the AI strength to Z1 — the default strength doesn’t let you play as any position except position 1.

Full instructions can be found here. The instructions aren’t very good, but the game is very simple to play and you will figure it out after a few games. However, even after figuring it out, you will still likely lose a lot of games to the very good AI! I think it took me over 15 games before I was finally able to scratch out a win!