I'm not sure to which extent solution has to be "generalized" and "plugable", but if you can afford refactoring of usage of your caches (replacing function calls with direct use of some variable), then consider the following:
//works for any CopyConstructible type of cache and any function
//which result should be cached
//(the arguments of the function have to be properly binded)
/**
* caching + lazy initialization
* we shouldn't allow copying of lazy<T>, because every copy initializes its own cache
* and this is not what intended most of the time
* T must be CopyConstructible
*/
template<class T>
class lazy: private boost::noncopyable
{
public:
lazy(boost::function0<T> _creator)
: creator(_creator) {}
/**
* aka is_cashed
*/
bool is_initialized()
{
return val;
}
operator T&()
{
if(!val)
val = creator();
return *val;
}
T& operator*()
{
if(!val)
val = creator();
return *val;
}
/**
* resets cache to update it next time it is used
*/
void reset()
{
val.reset();
}
private:
boost::function0<T> creator;
boost::optional<T> val;
};
//usage
//initialize caching and updating strategy
lazy<WebPage> cached_page(boost::bind(&Server::getPage, server));
server->OnPageUpdate = boost::bind(&OnPageUpdate, cached_page);
.....
//use cached_page everywhere as if it were regular variable of WebPage type
showPage(cached_page);
//--------------
void OnPageUpdate(lazy<WebPage>& page)
{
page.reset();
}
If you want to remove lazy initialization, then modify so that the cache is created in the constructor and method reset().