Click here to Skip to main content
15,890,123 members
Articles / Programming Languages / C++

MRU Cache Using C++ STL

Rate me:
Please Sign up or sign in to vote.
2.19/5 (6 votes)
14 Jun 2007CPOL 41.2K   241   13   4
Simple implementation of an MRU cache in C++ using STL.

Introduction

I suppose you know what an MRU cache is; otherwise, you wouldn't be reading this. This is an implementation of a very simple one using only STL.

To implement a cache, derive a subclass from this template class. As an implementor, you'd have to implement two methods:

  • HandleNonExistingKeyFetch - to handle cache misses. In this method, you access the real source of data behind the cache and return the value.
  • HandleItemRelease - (optional) called when an item is removed from the cache.
  • The cache class is a template of two types, a key and value (like hash map). The value type is the type of the resource, and the key type is the type of the resource address (this is how you fetch a resource).

Source Code

C++
#include <list>
#include <map>

/**
 * MRU Cache
 *
 * contains up to a fixed size of "Most Recently Used" items,
 * where items are assiciated with keys.
 * when asked to fetch a value that is not
 * in the cache, HandleNonExistingKeyFetch is called.
 * when an item is removed from the cache, HandleItemRelease is called.
 * implementor of this cache must provide those methods.
 *
 */
template <typename key_type, typename value_type>
class MruCache
{
public:
    
    const int maxLength;

    MruCache(int iMaxLength) : maxLength(iMaxLength) { }

    inline value_type FetchItem(key_type key) { return __fetch_item(key); }

    virtual ~MruCache() { Clear(); }

    /**
     * clear the cache.
     * for every item contained, HandleItemRelease is called.
     */
    virtual void Clear() { __clear(); }


protected:

    virtual void HandleItemRelease(key_type key, value_type value) { };

    virtual value_type HandleNonExistingKeyFetch(key_type key) = 0;

private:

    typedef struct _Entry
    {
        key_type key;
        value_type value;
    } Entry;

    typedef std::list<Entry> EntryList;
    EntryList listOfEntries;

    /**
     * for fast search in the cache.
     * this map contains pointers to iterators in EntryList.
     */
    typedef std::map<key_type, void*> ItrPtrMap;
    ItrPtrMap mapOfListIteratorPtr;

    value_type __fetch_item(key_type key)
    {
        Entry entry;
        EntryList::iterator* ptrItr = 
           (EntryList::iterator*) mapOfListIteratorPtr[key];
        if (!ptrItr)
        {
            if ( (int)listOfEntries.size() >= maxLength)
            {
                Entry lruEntry = listOfEntries.back();
                HandleItemRelease(lruEntry.key, lruEntry.value);
                listOfEntries.pop_back();
                delete mapOfListIteratorPtr[lruEntry.key];
                mapOfListIteratorPtr.erase(lruEntry.key);
            }

            entry.value = HandleNonExistingKeyFetch(key);
            entry.key = key;
            listOfEntries.push_front(entry);

            EntryList::iterator* ptrItr = new EntryList::iterator();
            *ptrItr = listOfEntries.begin();
            mapOfListIteratorPtr[key] = ptrItr;
        }
        else
        {
            entry = *(*ptrItr);
            listOfEntries.erase(*ptrItr);
            listOfEntries.push_front(entry);
            *ptrItr = listOfEntries.begin();
        }
        return entry.value;
    }

    virtual void __clear()
    {
        for (ItrPtrMap::iterator i=mapOfListIteratorPtr.begin(); 
             i!=mapOfListIteratorPtr.end(); i++)
        {
            void* ptrItr = i->second;
            EntryList::iterator* pItr = (EntryList::iterator*) ptrItr;
            HandleItemRelease( (*pItr)->key, (*pItr)->value );
            delete ptrItr;
        }
        listOfEntries.clear();
        mapOfListIteratorPtr.clear();
    }
};

The cache itself is a linked list. The newest elements are at the beginning of the list. The least recently fetched elements are at the end. A hash map is used to access the elements in "random access" time.

Usage Example

C++
char* HARD_DISK[] =
{
    "zero", "one", "two", "three", "four", 
    "five", "six", "seven", "eight", "nine", "ten"
};
class TestCache : public MruCache<int, char*>
{
public:
    TestCache(int iMaxLength) : MruCache<int, char*>(iMaxLength) { }
protected:
    virtual void HandleItemRelease(int key, char* value)
    {
        cout << "[DEBUG] key \"" << key 
             << "\" removed" << endl;
    }
    virtual char* HandleNonExistingKeyFetch(int key)
    {
        cout << "[DEBUG] key \"" 
             << key << "\" fetched" << endl;
        return HARD_DISK[key];
    }
};
int main()
{
    TestCache cache(4);
    cout << cache.FetchItem(1) << endl;
    cout << cache.FetchItem(2) << endl;
    cout << cache.FetchItem(3) << endl;
    cout << cache.FetchItem(4) << endl;
    cout << cache.FetchItem(5) << endl;
    // 1 is removed from the cache

    return 0;
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Israel Israel
A diet freak, social challanged usually without a women kind'a guy. Working at Microsoft as a SDE.
Also the author of FLAMEingo game.

Comments and Discussions

 
GeneralImprove the cache miss case Pin
JacquesLeroy29-Mar-10 6:26
JacquesLeroy29-Mar-10 6:26 
GeneralMemory Leaks ! Pin
JacquesLeroy29-Mar-10 6:15
JacquesLeroy29-Mar-10 6:15 
GeneralThere's a fatal design bug in this class Pin
JacquesLeroy19-Nov-09 23:40
JacquesLeroy19-Nov-09 23:40 
Questionis it really MRU? Pin
tibzzz25-Feb-09 2:31
tibzzz25-Feb-09 2:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.