I am a bit confused by the heap and by-value-versus-by-reference semantics involved in putting a std::string
key and a large struct
value into a container like boost::interprocess::map
.
Here is my situation, and some typedefs I'm using:
typedef std::string AreaKeyType;
typedef DATA_AREA_DESC AreaMappedType; // DATA_AREA_DESC is a big struct.
typedef std::pair<const AreaKeyType, AreaMappedType> AreaValueType;
typedef boost::interprocess::allocator<AreaValueType, boost::interprocess::managed_shared_memory::segment_manager> AreaShmemAllocator;
typedef boost::interprocess::map<AreaKeyType, AreaMappedType, std::less<AreaKeyType>, AreaShmemAllocator> AreaMap;
Here is how I'm inserting AreaValueType (which is a typedef for std::pair):
AreaValueType A(areaKey, arearec);
anAreaMap->insert(A);
I believe the above code copies A which is an std::pair on my local (non shared memory) stack into a shared memory area. Can I get a handle to that shared memory area inside the boost::interprocess::map or am I limited to fetching that record back whole and storing it whole? (In other words, can I store something like a structure into a boost interprocess map and then update a single byte inside that record, or do I have to only update the entire record by replacing all the bytes in a DATA_AREA_DESC struct, with entirely new bytes.)
Some further clarification:
I have a plain old ANSI C DLL export api that internally uses C++ and Boost::interprocess::map. The function is expected to create an item in the map and then return a handle. How can I insert something into the boost::interprocess::map and then return a handle to that entity, to non-C++ users, preferably cast to void*
or unsigned long
? All I can seem to do is fetch stuff from shared memory by looking up the std::string key value, and write a new record into memory. I'd like to instead be able to keep a reference to the shared memory object around.
If I can't directly do that, how would I do it indirectly? I suppose I could keep a non-shared-memory std::vector, and allocate a non-shared memory std::string holding the value of the areaKey, which is a std::string, and then do a cast of the void*
item back to std::string
and then use that to fetch a record out of the shared memory area. That all seems like more work than should be strictly necessary for something so elementary. Maybe boost::interprocess::map isn't the right choice for my requirements?
What have I tried? This, which compiles, but I have no idea if I am doing this right. Somehow I feel ugly inside dereferencing an ::iterator
returned from find
, and then immediately taking its address like so:
void ** handle; // actually a parameter in my api.
*handle = (void*)&(*anAreaMap->find(areaKey));
Update The above works. The very sensible advice in the answer below does NOT work however. Using boost::interprocess::string results in complete and total failure and crashes at runtime. Using std::string, which has no right to work unless the authors of Boost coded std::string support in especially, actually works great.
If handle
is supposed to be a pointer to the std::pair
in shared memory then your code will work provided you know that areaKey
is in the map. There's nothing wrong with it except you don't need the explicit cast (and if you do cast then static_cast<void*>()
would be preferred).
I haven't used boost::interprocess
but I think you will need to use boost::interprocess::string
or a std::basic_string
with a non-default allocator for your key. Unless boost::interprocess
does something fancy under the hood, using std::string
will put a pointer to local memory (for the string buffer) into shared memory which won't be meaningful in another process.
Here's a test program that uses a map with string keys:
#include <iostream>
#include <string>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
namespace bi = boost::interprocess;
#define SHARED_STRING 1 // set to 1 for interprocess::string, 0 for std::string
static const char *SHARED_MEMORY_NAME = "MySharedMemory";
static const char *SHARED_MAP_NAME = "MySharedMap";
int main(int argc, char *argv[]) {
#if SHARED_STRING
typedef bi::allocator<char, bi::managed_shared_memory::segment_manager> CharAllocator;
typedef bi::basic_string<char, std::char_traits<char>, CharAllocator> Key;
#else
typedef std::allocator<char> CharAllocator;
typedef std::basic_string<char, std::char_traits<char>, CharAllocator> Key;
#endif
typedef int Mapped;
typedef std::pair<const Key, Mapped> Value;
typedef bi::allocator<Value, bi::managed_shared_memory::segment_manager> MapAllocator;
typedef bi::map<Key, Mapped, std::less<Key>, MapAllocator> Map;
bi::managed_shared_memory *segment;
Map *map;
if (argc <= 1) {
// Create new shared memory segment.
bi::shared_memory_object::remove(SHARED_MEMORY_NAME);
segment = new bi::managed_shared_memory(bi::create_only, SHARED_MEMORY_NAME, 65536);
MapAllocator mapAllocator(segment->get_segment_manager());
map = segment->construct<Map>(SHARED_MAP_NAME)(std::less<Key>(), mapAllocator);
assert(map);
}
else {
// Open existing shared memory segment.
segment = new bi::managed_shared_memory(bi::open_only, SHARED_MEMORY_NAME);
map = segment->find<Map>(SHARED_MAP_NAME).first;
assert(map);
}
#if SHARED_STRING
CharAllocator charAllocator(segment->get_segment_manager());
#else
CharAllocator charAllocator;
#endif
while (true) {
std::string input;
if (!getline(std::cin, input))
break;
map->insert(std::make_pair(Key(input.begin(), input.end(), charAllocator), 0));
BOOST_FOREACH(const Value& value, *map)
std::cout << boost::format("('%s',%d)\n") % value.first % value.second;
}
delete segment;
bi::shared_memory_object::remove(SHARED_MEMORY_NAME);
return 0;
}
Run it with no arguments to create a new shared memory segment and with at least one argument to open an existing shared memory segment (a no-argument invocation must already be running). In both cases, the program will iteratively read a key from stdin
, insert an entry into the map, and write contents to stdout
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With