/** * @file llheteromap.h * @author Nat Goodspeed * @date 2016-10-12 * @brief Map capable of storing objects of diverse types, looked up by type. * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ */ #if ! defined(LL_LLHETEROMAP_H) #define LL_LLHETEROMAP_H #include #include #include // std::pair #include /** * LLHeteroMap addresses an odd requirement. Usually when you want to put * objects of different classes into a runtime collection of any kind, you * derive them all from a common base class and store pointers to that common * base class. * * LLInitParam::BaseBlock uses disturbing raw-pointer arithmetic to find data * members in its subclasses. It seems that no BaseBlock subclass can be * stored in a polymorphic class of any kind: the presence of a vtbl pointer * in the layout silently throws off the reinterpret_cast arithmetic. Bad * Things result. (Many thanks to Nicky D for this analysis!) * * LLHeteroMap collects objects WITHOUT a common base class, retrieves them by * object type and destroys them when the LLHeteroMap is destroyed. */ class LLHeteroMap { public: ~LLHeteroMap(); /// find or create template T& obtain() { // Look up map entry by typeid(T). We don't simply use mMap[typeid(T)] // because that requires default-constructing T on every lookup. For // some kinds of T, that could be expensive. TypeMap::iterator found = mMap.find(typeid(T)); if (found == mMap.end()) { // Didn't find typeid(T). Create an entry. Because we're storing // only a void* in the map, discarding type information, make sure // we capture that type information in our deleter. void* ptr = new T(); void (*dlfn)(void*) = &deleter; std::pair inserted = mMap.emplace(typeid(T), TypeMap::mapped_type(ptr, dlfn)); // Okay, now that we have an entry, claim we found it. found = inserted.first; } // found->second is the std::pair; second.first is the void* // pointer to the object in question. Cast it to correct type and // dereference it. return *(static_cast(found->second.first)); } private: template static void deleter(void* p) { delete static_cast(p); } // What we actually store is a map from std::type_index (permitting lookup // by object type) to a void* pointer to the object PLUS its deleter. typedef std::unordered_map> TypeMap; TypeMap mMap; }; #endif /* ! defined(LL_LLHETEROMAP_H) */