Reputation: 1754
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
Upvotes: 0
Views: 299
Reputation: 171243
Firstly, your question says "for a std::allocator" but this is not a std::allocator
, it's a ShmAllocator
. std::allocator
is 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::allocator
is 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 a
of type ShmAllocator<T>
the statement ShmAllocator<T> a1(a);
is valid, with the post-condition that a1 == a
, and for an object b
of type ShmAllocator<U>
the statement ShmAllocator<T> a(b);
is valid, with the post-condition that ShmAllocator<U>(a) == b
and a == 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 mHandles
as shared_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 allocate
you need to use a different value for name
or 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 time allocate
is called, rather than create a new region on every call to allocate
. 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 call allocate
to get a pointer to an already constructed object. That's confusing, allocate
should 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.
Upvotes: 1