The implementation of a std::allocator (based on MAllocator) backed by Windows named shared memory, adds a couple of items to MAllocator.
-
An additional parameter name for allocate(), alters the signature over std::allocator. Is this ok, considering that allocators are typically template parameters (errors will be caught at compile time)?
template<class T> T *allocate(const size_t n, const char* name); -
State is maintained in the form of a map of pointers to handles for deallocate(). The allocator denys equality with other instances to preserve state integrity. Is this sufficient or are additional safeguards needed?
typedef std::map<uintptr_t, HANDLE> HandleMap; HandleMap mHandles; deallocate(T *const p, size_t n = 0) { if (mHandles.find((uintptr_t)p) == mHandles.end()) //we don't own this pointer { std::ostringstream msg; msg << "Error in deallocate(): Invalid pointer - we don't own it - we can't delete it!"; throw std::exception(msg.str().c_str() ); } unmapView(p); closeHandle(p); mHandles.erase((uintptr_t)p); }
Full code on github
Firstly, your question says “for a std::allocator” but this is not a
std::allocator, it’s aShmAllocator.std::allocatoris a class template provided by the standard library, your allocator is not that class template, it’s a different type. I think you mean “for an allocator” or just “for a standard allocator”, which refers to a type conforming to the allocator requirements (std::allocatoris just one example of such a type.)This fails to meet the allocator requirements, specifically a copy is not equal to the original object. If you plan to use this allocator with code that expects a standard-conforming allocator it could fail. If you don’t plan to use it with code that expects a standard allocator, then why bother copying its interface?
The requirements for the copy constructor require that for an object
aof typeShmAllocator<T>the statementShmAllocator<T> a1(a);is valid, with the post-condition thata1 == a, and for an objectbof typeShmAllocator<U>the statementShmAllocator<T> a(b);is valid, with the post-condition thatShmAllocator<U>(a) == banda == ShmAllocator<T>(b).These requirements are necessary because containers need to be able to rebind allocators and make copies which must be able to free each others memory. Equality for stateful allocators should be dependent on value not identity.
To make your allocator meet the requirements you could store
mHandlesasshared_ptr<mHandles>so that copies of the allocator can all share the same collection.How do you plan to use this allocator? I don’t know the Windows API functions, but it appears that every time you call
allocateyou need to use a different value fornameor you will get the same pointer returned, which means it can’t be used by standard containers. I would expect a shared-memory allocator to create a shared-memory region in its constructor (or be constructed with a reference to some existing region, e.g. see Boost.Interprocess allocators) then divide that region up and return different chunks of it every timeallocateis called, rather than create a new region on every call toallocate. Is this intended to be user to share objects between processes? How will another process get access to an existing object? It seems the other process would have to callallocateto get a pointer to an already constructed object. That’s confusing,allocateshould give new, uninitialized memory. A shared-memory allocator would usually define a custom pointer type which stores an offset into the shared-memory region not an absolute address, so that when such a pointer is used in a different process the offset is still valid, even if the region is mapped to a different address.