Search code examples
c++multimapstdmapreference-counting

C++: Reference counting values in a std::map; is std::multimap a better choice?


Currently I have implemented a reference-counting cache of the values of the map like so:

//filename or name of bitmap, reference count, memory location...
std::map<std::string, std::pair<long, BITMAP*> > _cache;

Is using a std::multimap a better choice?

//filename or name of bitmap, memory location...
std::multimap<std::string, BITMAP*> _cache;

Or just a different way of doing it?

-- EDIT --

Here's the specific class for clarity of my intentions It is intended to be a private class that is strictly utility to the rest of the code that the user never sees. To them, they are just creating a Sprite. NOTE: The BITMAP struct is considered private and the only way to create/destroy/modify one is through one of the many functions in the 3rd-party C library that REQUIRE the use of raw pointers.

BitmapCache.h

#ifndef A2DE_CBITMAPCACHE_H
#define A2DE_CBITMAPCACHE_H

#include "../a2de_vals.h"
#include <allegro/file.h>
#include <allegro/gfx.h>
#include <allegro/draw.h>
#include <allegro/datafile.h>
#include <allegro/color.h>

#include <map>
#include <utility>
#include <string>

struct BITMAP;

_A2DE_BEGIN

class BitmapCache {
public:
    static BITMAP* GetBitmap(std::string filename);
    static BITMAP* StoreBitmap(std::string name, BITMAP* bmp);
    static BITMAP* RetrieveBitmap(std::string name);
    static std::string GetBitmapName(BITMAP* file);
    static void RemoveBitmap(std::string name);

protected:
private:
    static std::map<std::string, std::pair<long, BITMAP*> > _cache;
    static void CleanCache();

};

_A2DE_END

#endif

BitmapCache.cpp

#include "CBitmapCache.h"

#include <algorithm>
#include <map>

_A2DE_BEGIN

//filename or name of bitmap, reference count, memory location...
typedef std::map<std::string, std::pair<long, BITMAP*> > MapStrBmp;
typedef MapStrBmp::iterator MapStrBmpIter;

MapStrBmp BitmapCache::_cache;

BITMAP* BitmapCache::GetBitmap(std::string filename) {
    //Return NULL if a bad filename was passed.
    if(filename.empty()) return NULL;
    if(exists(filename.c_str()) == false) return NULL;

    //Reduce incorrect results by forcing slash equality.
    filename = fix_filename_slashes(&filename[0]);

    //Clean the cache if it's dirty.
    CleanCache();

    //Search for requested BITMAP.
    MapStrBmpIter _iter = _cache.find(filename);

    //If found, return it.
    if(_iter != _cache.end()) {
        _iter->second.first++;
        return _iter->second.second;
    }

    //Otherwise, create it, store it, then return it.
    BITMAP* result = load_bmp(filename.c_str(), NULL);
    if(result == NULL) return NULL;
    _cache.insert(std::make_pair(filename, std::make_pair(static_cast<long>(1), result)));
    return result;
}

BITMAP* BitmapCache::StoreBitmap(std::string name, BITMAP* bmp) {
    if(name.empty() || bmp == NULL) return NULL;

    CleanCache();
    name = fix_filename_slashes(&name[0]);
    MapStrBmpIter _iter = _cache.find(name);
    if(_iter != _cache.end()) {
        _iter->second.first++;
        return _iter->second.second;
    }

    _cache.insert(std::make_pair(name, std::make_pair(static_cast<long>(1), bmp)));
    return bmp;
}
BITMAP* BitmapCache::RetrieveBitmap(std::string name) {
    if(name.empty()) return NULL;

    name = fix_filename_slashes(&name[0]);
    MapStrBmpIter _iter = _cache.find(name);
    if(_iter != _cache.end()) {
        _iter->second.first++;
        return _iter->second.second;
    }
    return NULL;
}

void BitmapCache::RemoveBitmap(std::string name) {
    if(name.empty()) return;

    name = fix_filename_slashes(&name[0]);
    MapStrBmpIter _iter = _cache.find(name);

    if(_iter != _cache.end()) {
        _iter->second.first--;
        CleanCache();
    }
}

std::string BitmapCache::GetBitmapName(BITMAP* file) {
    if(file == NULL) return std::string("");

    CleanCache();
    MapStrBmpIter b = _cache.begin();
    MapStrBmpIter e = _cache.end();
    for(MapStrBmpIter _iter = b; _iter != e; ++_iter) {
        if(_iter->second.second != file) continue;
        return _iter->first;
    }
    return std::string("");
}

void BitmapCache::CleanCache() {

    //Clean the cache of any bitmaps that are no longer referenced.
    MapStrBmpIter b = _cache.begin();
    MapStrBmpIter e = _cache.end();
    for(MapStrBmpIter _iter = b; _iter != e; /* DO NOTHING */ ) {
        if(_iter->second.first > 0) {
            ++_iter;
            continue;
        }
        destroy_bitmap(_iter->second.second);
        _iter->second.second = NULL;
        _cache.erase(_iter++);
    }
}

_A2DE_END

Solution

  • Well, it turns out I can't use STL smart pointers with what I am trying to do. After re-working the above code to use smart pointers the program kept crashing on close. It turns out that the allegro 4.2 library API specifically states that attempting to call allegro methods (specifically ones that deal with bitmaps...) after allegro has been deinitialized will crash the program. Smart pointers only destroy themselves (and try to call their contained pointer's deleter methods) after they've run out of scope...after the program has ended and allegro has been deinitialized, which in turn crashes the program.

    So for now, the current solution of rolling my own implementation of reference counting is the work-around.

    Thanks for the advice and the help anyway.