I need some kind of class to manage the lifetime of singleton objects. I tried adding a method like this to my classes
static const FixedOutlineRect2D &instance() {
static const FixedOutlineRect2D self{};
return self;
}
Which has the nice property of constructing the object the first time it's called, but then it's not destroyed until the program is terminated which is messing up the order of deletions.
So I was thinking I could have some kind of "singleton factory" that would tie the lifetimes of all the objects to the factory and then I can destroy the whole thing as needed.
Here's what I've got so far:
class SingletonFactory {
public:
template<class T>
const T &get() {
if(!_map.template contains(typeid(T))) {
_map.template emplace(typeid(T), new T);
}
return *static_cast<const T*>(_map.at(typeid(T)));
}
~SingletonFactory() {
for(const auto& p : _map) {
delete p.second;
}
}
private:
std::unordered_map<std::type_index, const void*> _map{};
};
Usage would be like:
const Rect& rect = factory.get<FixedOutlineRect2D>();
Which would either coinstruct a new instance if one doesn't yet exist or return an existing instance.
But what I can't figure out is how to delete the instances. I'm getting an error:
Cannot delete expression with pointer-to-'void' type 'const void *'
Which makes sense because it can't know how many bytes to free unless it knows the type.
Can I get the type back out of the key so that I can cast and delete it? Or is there a better way to do what I'm trying?
This compiles and runs now:
class SingletonFactory {
public:
template<typename T, typename... Args>
const T &get(Args &&... args) {
// std::decay_t should be used
auto &cache = getCache<T, std::decay_t<Args>...>();
// Creating tuple from the arguments
auto arguments = std::forward_as_tuple(std::forward<Args>(args)...);
// Search for object in the cache
auto it = cache.find(arguments);
if (it != cache.end()) {
// Found. Return.
return *it->second;
}
// Not found. Add to cache.
auto *object = new T(std::forward<Args>(args)...);
cache.emplace(std::make_pair(std::move(arguments), object));
return *object;
}
private:
template<class T, class...Args>
static std::map<std::tuple<Args...>, const T *> &getCache() {
static std::map<std::tuple<Args...>, const T *> cache; // only run once
return cache;
}
};
(stolen and slightly modified from C++ templates std::tuple to void* and back)
But I still don't know how to clear out the caches... if I leave them as std::shared_ptr
as OP had them it doesn't help exactly, they're still destructed at the end of the program. How can I iterate over all the static caches? If I had a map of caches/maps I could do it, but I don't think I can.