Keeping track of C++ templates

So I needed to build a complex template. I wanted to build a map which also operated as a cache: as the contents of the map hit a limit on the number of objects in the map, the last referred to objects are automatically erased from the map. (What I’m doing is writing a bit of code to insert itself between a Sqlite database and some accessor code, but the pattern of access would cause a lot of unnecessary ‘select’ statements. Rather than trying to load my database in memory and somehow manage the synchronization from a bunch of places, a database cache seemed to make the most sense for my application.)

I ran into two interesting things which I’m noting here for future reference.

Keep the interior value declarations simple by using the typedef keyword.

At one point I found myself writing the following code:

   std::pair<std::map<Key,std::pair<std::list<Key>::iterator, T> >, bool> ret;

And of course the code didn’t work–which was really irritating, because I was getting all sorts of weird messages that had something to do with whatever it was I was declaring. Arg!

My lesson: break the declarations into class-scoped typedef declarations:

typedef std::list<Key> CacheList;
typedef CacheList::iterator CacheIter;
typedef std::pair<CacheIter,T> MapVal;

typedef std::map<Key,MapVal> Map;
typedef Map::iterator MapIter;

Then the declaration of the return value of a std::map::insert() call becomes easy:

std::pair<MapIter,bool> ret;

Once I did this, I learned my second lesson:

Sometimes you need to declare a type as a typename for some odd reason.

I’m sure there is some C++ template expert out there who will tell me why–and it will be blindingly obvious when I hear the explanation. I’m sure it has something to do with the nature of the declarations and how they are used within a template class declaration. But for now, it seems to me that under some cases you need this ‘typename’ keyword for reasons that aren’t obvious to me–but once they’re there, all is good in the universe.

And for those experts out there, I’m sure you spotted what I screwed up in the above listing right away. For the rest of the class, I needed to insert ‘typename’ for the iterator typedefs:

typedef std::list<Key> CacheList;
typedef typename CacheList::iterator CacheIter;
typedef std::pair<CacheIter,T> MapVal;
		
typedef std::map<Key,MapVal> Map;
typedef typename Map::iterator MapIter;

Without the ‘typename’ declaration, I was getting these mysterious errors:

type std::list<Key, std::allocator<_CharT> >' is not derived from type 'mapcache<Key,T>'

For reference, the class that I put together to do this simple caching scheme is here. Licensed with a BSD-style license in case anyone else finds it useful.

The Big Three of C++ Classes

I ran into an interesting problem while working on some code, and I thought it would probably be a good idea to, well, document the problem so I can remember it in the future.

The problem was this: I have a C++ class which is the key index in a std::map class. This C++ class wraps a Mac CFUUID object and provides a unique index based on UUID. And it was mysteriously crashing.

The root of the problems I had with my class were this:

(1) I wasn’t declaring the big three entry points for a C++ class to be used as an object within a C++ container class, like a std::map class. For the handful of people out there who don’t know, these entry points are the copy constructor, the copy operator, and the destructor. In my case, I was missing the following:

class CStdUuid {
    public:
        CStdUuid(const CStdUuid &);
        ~CStdUuid();
        CStdUuid &operator = (const CStdUuid &);

(2) In order to be used properly for comparing keys within a map, I also needed to properly declare the following compare operators:

        bool operator == (const CStdUuid &) const;
        bool operator < (const CStdUuid &) const;

Now I’m not quite sure off the top of my head if I only need to declare these two operators to be used as a key in a std::map class, or if this just happens to be true for my particular implementation of the standard C++ template library. I’m sure a quick search with Google will yield an answer.

But why my particular implementation was crashing was simple: my implementation of the CStdUuid::operator < operator was screwed up. Because my class was returning true if the items were equal, and not when my class was less than the provided class, the std::map class became confused, and was crashing mysteriously.

Which is today’s lesson of the day: if std::map is crashing mysteriously, check your compare operators, since the tree structure inside std::map may be getting munged by an incorrectly declared compare operation.

So… How many languages can you use?

Let’s see: Java + JSP for the server. C++ for the core desktop and phone engine. C++ for the Windows and WinCE version. Objective-C++ for the Mac and iPhone version.

Sounds just about right for a custom client-server system targeting multiple platforms: it’s about using the right tool for the job, and keeping things as simple as they can be–but no simpler.