Как я могу вернуть непрозрачный дескриптор (void* или dword), который может быть приведен обратно к элементу значения, хранящемуся в карте boost::interprocess?

Я немного сбит с толку семантикой "куча" и "значение по сравнению с ссылкой", вовлеченной в 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 полностью новыми байт.)

Некоторые дальнейшие разъяснения:

  1. У меня есть простой старый API экспорта ANSI C DLL, который внутренне использует C++ и Boost::interprocess::map. Ожидается, что функция создаст элемент на карте, а затем вернет дескриптор. Как я могу вставить что-то в boost:: interprocess:: map и затем вернуть дескриптор этой сущности пользователям, не являющимся C++, предпочтительно приведение к void* или же unsigned long? Все, что я могу сделать - это извлечь данные из общей памяти, посмотрев значение ключа std:: string и записать новую запись в память. Вместо этого я хотел бы иметь возможность хранить ссылку на объект общей памяти.

  2. Если я не могу сделать это напрямую, как бы я сделал это косвенно? Я полагаю, я мог бы сохранить 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 в частности, на самом деле прекрасно работает.

1 ответ

Если 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,

Другие вопросы по тегам