November 14, 2023

Let's learn USB! -- C++ templates

The idea is as follows. Suppose you have written a cool class that does something with (let's say) integers. Now you would like the class to work with doubles. Without templates you would have to work up a new class just for doubles.

With templates, you can make the type a class works with generic. You have a class that deals with "things" and you can tell it what kind of things.

An example is the vector class that comes with C++.

vector vec = {1, 2, 3, 4, 5};
Here we declare (and initialize a vector of integers. Notice the type inside the angle brackets. That is the syntax for a template class. Note that a C++ vector is an alternative to an array with extra features.

As a user (consumer) of some template class, all you need to know is that you shove the name of the type inside the angle brackets.

My silly example

I want to make a "saver" class that will be used as "Saver". It will be like a one element stack that will allow you to save and restore a single value of some type. The idea is not to produce something useful but to illustrate templates with a minimal amount of example code. Here you go:
#include <cstdio>
#include <string>

using namespace std;

template <typename vtype>
class Saver
{
    public:
        Saver ( vtype arg )
        {
            _bogus = arg;
            _empty = true;
        }
        void save ( vtype );
        vtype restore ( void );
    private:
        bool _empty;
        vtype _thing;
        vtype _bogus;
};

template <typename vtype>
inline void Saver<vtype>::save ( vtype xx )
{
        _thing = xx;
        _empty = false;
}

template <typename vtype>
inline vtype Saver<vtype>::restore ( void )
{
        vtype rv;

        if ( _empty )
            rv = _bogus;
        else
            rv = _thing;

        _empty = true;
        return rv;
}

int
main ()
{
    string x;

    Saver<string> s ( "fooey" );
    s.save ( "potatoe" );
    x = s.restore ();
    printf ( "%s\n", x.c_str() );
    x = s.restore ();
    printf ( "%s\n", x.c_str() );
}
Even with all of my effort to make this short, it got a bit long. But it is full, working, tested code.

By no means vital, but of passing interest, I use the C++ fundamental "bool" type, and I feel very proud to have done so.

The thing to focus on are the lines with "template". There are three of these and they precede the class definition and the individual method definitions. They indicate that the "virtual type" with be indicated by "vtype" (I could have chosen any name, but I liked "vtype"). I then use this name where the actual type will be substituted by the compiler.

I faced a dilemna when writing the restore method. I wanted to return some kind of "nasty value" if the container was empty, but when any type is possible, how can I put in something like the famous 999 value so often used for integers. I punted by letting the caller specify the bogus value when the object is instantiated. Once you see how "vtype" is specified, the scheme here should be clear enough. Notice that we could write main() like this:

int
main ()
{
    string x;
    int n;

    Saver<string> s ( "fooey" );
    s.save ( "potatoe" );
    x = s.restore ();
    printf ( "%s\n", x.c_str() );
    x = s.restore ();
    printf ( "%s\n", x.c_str() );

    Saver<int> j ( 999 );
    j.save ( 13 );
    printf ( "%d\n", j.restore() );
}
Here we use the same class, first for strings, then for integers. We could use it for doubles or even classes other than "string" (remember that "string" is just a C++ class, albeit part of the standard library).

The End

This ends my little C++ crash course. I have enjoyed learning about this language that I have been insulting and shunning for many years. There is no doubt much more to be learned, but this should suffice me for working on the USB papoon project, which was my goal after all. If you find mistakes or confusion in this, as you very well might, let me know and I will do what I can about it.
Feedback? Questions? Drop me a line!

Tom's Computer Info / tom@mmto.org