I realised that what I was doing wasn't particularly smart, so I switched to a different approach. This costs more dynamic allocations but has a much simpler implementation:
#pragma once
#include <boost/utility.hpp>
#include <set>
#include <map>
//! A map of types to values.
//!
//! Associative container which allows mapping of types to values of that
//! type.
class TypeMap : boost::noncopyable {
typedef void (*destruct_func)(void*);
class TypeMapStaticInstance : boost::noncopyable {
destruct_func destroy_;
std::map<TypeMap const*, void*> map_;
public:
TypeMapStaticInstance(destruct_func f)
: destroy_(f) {}
void*& get(TypeMap const* tm) {
return map_[tm];
}
void const* cget(TypeMap const* tm) const {
auto element = map_.find(tm);
if (element != map_.end())
return element->second;
return nullptr;
}
void remove(TypeMap const* tm) {
auto element = map_.find(tm);
destroy_(element->second);
map_.erase(element);
}
};
template<typename T>
class TypeMapDetail {
TypeMapDetail() = delete;
static void destroy_impl(void* p) {
delete static_cast<T*>(p);
}
public:
static TypeMapStaticInstance map_;
static T& get(TypeMap const* p) {
auto& element = map_.get(p);
if (!element)
element = new T();
return *static_cast<T*>(element);
}
static T const* cget(TypeMap const* p) {
return static_cast<T const*>(map_.cget(p));
}
};
std::set<TypeMapStaticInstance*> members_;
public:
//! Retrieve the data associated with the given type.
template<typename T>
T& get() {
members_.insert(&TypeMapDetail<T>::map_);
return TypeMapDetail<T>::get(this);
}
template<typename T>
T const* cget() const {
return TypeMapDetail<T>::cget(this);
}
~TypeMap() {
for (auto m : members_) {
m->remove(this);
}
}
};
template<typename T>
TypeMap::TypeMapStaticInstance TypeMap::TypeMapDetail<T>::map_(TypeMap::TypeMapDetail<T>::destroy_impl);
Comments:
- One of the reasons I think this approach may be better is that I don't see any undefined behaviour being invoked. Am I correct about this?
- Now that standard containers can have stateful allocators, would having each contain
destruct_func
(and probablyconstruct_func
) make sense?
Other issues as always welcome.