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.