Я немного сбит с толку семантикой «куча» и «значение по сравнению с ссылкой», вовлеченной в std::string
ключ и большой struct
значение в контейнер, как boost::interprocess::map
,
Вот моя ситуация и некоторые typedefs, которые я использую:
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;
Вот как я вставляю AreaValueType (который является typedef для std :: pair):
AreaValueType A(areaKey, arearec);
anAreaMap->insert(A);
Я полагаю, что приведенный выше код копирует A, который является std :: pair в моем локальном (не совместно используемой памяти) стеке, в область совместно используемой памяти. Могу ли я получить указатель на эту область разделяемой памяти внутри boost :: interprocess :: map или я ограничен извлечением этой записи целиком и сохранением ее целиком? (Другими словами, могу ли я сохранить что-то вроде структуры в карте межпроцессного ускорения и затем обновить один байт внутри этой записи, или мне нужно только обновить всю запись, заменив все байты в структуре DATA_AREA_DESC полностью новой байт.)
Некоторые дальнейшие разъяснения:
У меня есть обычный старый API экспорта ANSI C DLL, который внутренне использует C ++ и Boost :: interprocess :: map. Ожидается, что функция создаст элемент на карте, а затем вернет дескриптор. Как я могу вставить что-то в boost :: interprocess :: map и затем вернуть дескриптор этой сущности пользователям, не являющимся C ++, предпочтительно приведение к void*
или же unsigned long
? Все, что я могу сделать — это извлечь данные из общей памяти, посмотрев значение ключа std :: string и записать новую запись в память. Вместо этого я хотел бы иметь возможность хранить ссылку на объект общей памяти.
Если я не могу сделать это напрямую, как бы я сделал это косвенно? Я полагаю, что я мог бы сохранить std :: vector без общей памяти и выделить std :: string без общей памяти, содержащий значение areaKey, который является std :: string, а затем выполнить приведение void*
пункт назад к std::string
и затем используйте это, чтобы извлечь запись из области общей памяти. Все это выглядит как большая работа, чем это должно быть строго необходимо для чего-то столь элементарного. Может быть, boost :: interprocess :: map не подходит для моих требований?
Что я пробовал? Это компилируется, но я понятия не имею, правильно ли я делаю. Каким-то образом я чувствую себя ужасно внутри разыменования ::iterator
вернулся из find
, а затем сразу же взять его адрес так:
void ** handle; // actually a parameter in my api.
*handle = (void*)&(*anAreaMap->find(areaKey));
Обновить Выше работает. Однако очень разумный совет в ответе ниже НЕ работает. Использование boost :: interprocess :: string приводит к полному и полному отказу и сбоям во время выполнения. Использование std :: string, которое не имеет права работать, если только авторы Boost не закодировали поддержку std :: string в частности, на самом деле прекрасно работает.
Если handle
должен быть указателем на std::pair
в общей памяти, тогда ваш код будет работать предоставлена ты знаешь что areaKey
находится на карте. В этом нет ничего плохого, за исключением того, что вам не нужно явное приведение (и если вы делаете приведение static_cast<void*>()
будет предпочтительным).
Я не использовал boost::interprocess
но я думаю, что вам нужно будет использовать boost::interprocess::string
или std::basic_string
с нестандартным распределителем для вашего ключа. Если не boost::interprocess
делает что-то модное под капотом, используя std::string
поместит указатель на локальную память (для строкового буфера) в разделяемую память, которая не будет иметь смысла в другом процессе.
Вот тестовая программа, которая использует карту со строковыми ключами:
#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;
}
Запустите его без аргументов, чтобы создать новый сегмент совместно используемой памяти, и, по крайней мере, с одним аргументом, чтобы открыть существующий сегмент совместно используемой памяти (вызов без аргументов уже должен выполняться). В обоих случаях программа будет итеративно читать ключ из stdin
вставьте запись в карту и запишите stdout
,
Других решений пока нет …