Inside a boost::interprocess::managed_shared_memory
, I am trying to create boost::unordered_map
inside another boost::unordered_map
as value, having key as std::string
for both maps. This Map in Map inside a shared memory segment gets accessed by two different processes fetch values from both outer & inner maps.
Below is my implementation & want to know if this is possible/right way or any other better way possible?
boost::interprocess::managed_shared_memory segment(boost::interprocess::open_or_create, "BOOST_SHM", 65536);
typedef std::string KeyType;
typedef std::string ValueType;
typedef std::pair<const KeyType, ValueType> MapType;
typedef boost::interprocess::allocator<MapType, boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
typedef boost::unordered_map<KeyType, ValueType, boost::hash<KeyType>, std::equal_to<KeyType>, ShmemAllocator> InMap;
ShmemAllocator alloc_inst(segment.get_segment_manager());
InMap *inside_map = segment.construct<InMap>("SHM_IN_MAP")(3, boost::hash<KeyType>(), std::equal_to<KeyType>(), alloc_inst);
typedef std::pair<const KeyType, MapType> MIMType;
typedef boost::interprocess::allocator<MIMType, boost::interprocess::managed_shared_memory::segment_manager> MIMShmemAllocator;
typedef boost::unordered_map<KeyType, MapType, boost::hash<KeyType>, std::equal_to<KeyType>, MIMShmemAllocator> OutMap;
//MIMShmemAllocator alloc_inst(segment.get_segment_manager()); /*Commented due to Error*/
OutMap *outside_map = segment.construct<OutMap>("SHM_OUT_MAP")(3, boost::hash<KeyType>(), std::equal_to<KeyType>(), alloc_inst);
Other details:
gcc version 4.8.3 20140911 (Red Hat 4.8.3-9) (GCC) on CentOS 7, BOOST_LIB_VERSION "1_58"
Ok.
So there were a few basic errors, and possibly some confusion.
Next, there are some power tricks that make using nested containers with custom (stateful) allocators much more convenient.
Here's the roll-up of all three hints in a working sample that hopefully helps!
Your strings must use shared memory allocators too
Otherwise the data would be illegal to use in another process. Using the strings would result in Undefined Behaviour.
At the very least, make your strings use the shared memory allocator:
The map allocators were overspecified. The actual node-types wrapping the
pair<K const, v>
elements in a map are implementation defined anyways. So how do maps know how to allocate these nodes?They rebind allocators: see
rebind
in the docs hereSo, you can just pass
Alloc<void>
. Or the same allocator as for theShared::String
. The map will figure it out:Now for the power tips.
Passing stateful allocators all the freaking time is annoying. It makes code a mess. Luckily, c++11 (and Boost Containers for c++03) has you covered:
scoped_allocator_adaptor<T...>
allocator_type
uses_allocator<T>
traitThese helpers can make your life a lot easier. They do this by passing the allocator down to element type constructors when applicable. Automatically. Again, implicit conversions from rebound allocator types make things work.
So, you can actually just construct one outer map with the correct allocator (make it
Scoped
) and one key, and from there you don't even have to keep specifying allocators.Here's a full demo:
Live On Coliru
Actually, to make it work on Coliru, we need to use a mapped file instead:
Live On Coliru
Run it a few times:
Second run:
Note how each run successfully appends
value
to 9 keys in 3 inner maps.