|
/*-----------------------------------------------------------------------------------------------------------------------
// By: Kenneth MacLean 2012
//Usage
SmartMap<string,Foo> fooMaker;
Observer<Foo> fooObserver;
{
Retain<Foo> foo1 = fooMaker["id 001"]; // creates a Foo with key "id 001"
Retain<Foo> foo2 = fooMaker["id 001"]; // doesn't need to create a Foo as it already exists
Retain<Foo> foo3 = foo2; // now we have 3 references to the same Foo
cout << foo1.get()->name << " has " << foo1.use_count() << " references" << endl;
// foo1, foo2, foo3 all have a reference count of 3 as they all contain the same Foo
// output: "id 001 has 3 references"
fooObserver = foo1;
cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
// notice that we still have 3 references even though we've added an observer
// that's because the observer has no baring on the reference count
// output: "id 001 has 3 references"
{
Retain<Foo> foobar = fooMaker["id 999"];
fooObserver = foobar;
cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
// the foo observer is now observing a foobar and reports that it has only 1 reference
// output: "id 999 has 1 references"
foobar = foo1;
cout << "fooObserver expired? " << std::boolalpha << " and has " << fooObserver.use_count() << " references" << endl;
// fooObserver is still pointing to a Foo with the key "id 999" but foobar is
// now pointing to "id 001", so the Foo with the key "id 999" has gone out of scope and
// deleted itself (expired). Remember, just because fooObserver is still pointing to
// it doesn't mean it is still in scope, because observers have no baring on the reference count
// output: "fooObserver expired? yes and has 0 references"
fooObserver = foo1;
cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
// since foobar has been added to the Foo with the key "id 001" we now have a reference count of 4
// output: "id 001 has 4 references"
}
fooObserver = foo1;
cout << fooObserver._Get()->name << " has " << fooObserver.use_count() << " references" << endl;
// foobar is now out of scope so the Foo with the key "id 001" has dropped 1 reference
// output: "id 001 has 3 references"
}
cout << "fooObserver expired? " << std::boolalpha << fooObserver.expired() << " and has " << fooObserver.use_count() << " references" << endl;
// all the foos have gone out of scope so the Foo with the key of "id 001" has been deleted
// output: "fooObserver expired? yes and has 0 references"
------------------------------------------------------------------------------------------------------------------------*/
#pragma once
#include <map>
#include <memory>
#define Retain std::shared_ptr
#define Observer std::weak_ptr
template < typename Key, typename T >
class SmartMap
{
protected:
// these are called observers because they are automatically deleted when
// the requester has finished with it (goes out of scope).
// Each object must have a unique key as 2 references with the
// same key point to the same object
std::map<Key,Observer<T> > observers;
public:
// example: below, foo and sameFoo are pointing to the same obj because they share the same key
// Retain<Foo> foo = fooMap["id 0001"]; // creates a Foo with key "id 0001"
// Retain<Foo> sameFoo = fooMap["id 0001"]; // doesn't need to create a Foo as it already exists
// Retain<Foo> differentFoo = fooMap["id 0002"]); // creates a Foo with key "id 0002"
Retain<T> operator[](Key key)
{
// First it checks to see if an observer has already been created
// using the key, remember that the same key will return the same object.
// If it maps to an observer and it has expired, this means
// that all references that belong to this key have gone out of scope,
// so a new object will have to be created. If an Observer<T> has been found
// then we can create a Retain<T> from it by calling lock()
auto it = observers.find(key);
if(it != observers.end() && !(*it).second.expired())
return (*it).second.lock();
Retain<T> newT = Retain<T>(new T(key));
observers[key] = newT;
return newT;
}
// This may need to be called if the map has a lot of expired entries
void flushExpired()
{
for(auto it = observers.begin(); it!= observers.end(); )
{
if((*it).second.expired())
observers.erase(it++);
else
it++;
}
}
};
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
I have been programming since I was a kid, text based adventure games on the C64 in those good old days then went on to complete CS degree. Since then I've been a contract programmer for the last 12 years developing for web, mobile, desktop and game consoles. Currently I'm working for Laservision were I get to play with lasers and video projectors for fun and profit.
So glad C++ is going through a bit of a renaissance at the moment as it's my favourite language