2013-03-24 10 views
7

Sono un po 'confuso dall'heap e dalla semantica valore-per-riferimento implicita nel mettere una chiave std::string e un grande valore struct in un contenitore come boost::interprocess::map.Come posso restituire un handle opaco (void * o dword) che può essere restituito a un elemento di valore memorizzato in una mappa di boost :: interprocess?

Ecco la mia situazione, e alcuni typedef sto usando:

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; 

Ecco come sto inserendo AreaValueType (che è un typedef per std :: coppia):

AreaValueType A(areaKey, arearec); 
anAreaMap->insert(A); 

Credo che il codice precedente copi A che è una coppia std :: pair sul mio locale (memoria non condivisa) in un'area di memoria condivisa. Posso ottenere un handle su quell'area di memoria condivisa all'interno di boost :: interprocess :: map o sono limitato a recuperare tutto il record e conservarlo intero? (In altre parole, posso memorizzare qualcosa come una struttura in una mappa interprocessa boost e quindi aggiornare un singolo byte all'interno di quel record, oppure devo solo aggiornare l'intero record sostituendo tutti i byte in una struttura DATA_AREA_DESC, con completamente nuovo byte)

Alcuni ulteriori chiarimenti:.

  1. ho una pianura vecchio API di esportazione ANSI C DLL che utilizza internamente C++ e boost :: :: interprocess mappa. Si prevede che la funzione crei un elemento nella mappa e quindi restituisca un handle. Come posso inserire qualcosa nella mappa boost :: interprocess :: e quindi restituire un handle a quell'entità, a utenti non C++, preferibilmente cast a void* o unsigned long? Tutto quello che posso sembrare è di recuperare le cose dalla memoria condivisa cercando il valore della chiave std :: string, e scrivere un nuovo record nella memoria. Mi piacerebbe invece essere in grado di mantenere un riferimento all'oggetto di memoria condivisa in giro.

  2. Se non riesco a farlo direttamente, come lo farei indirettamente? Suppongo che potrei conservare una memoria non condivisa std :: vector e allocare una memoria non condivisa std :: string che contiene il valore dell'areaKey, che è una std :: string, e quindi fare un cast di void* voce di nuovo a std::string e quindi utilizzarlo per recuperare una registrazione dall'area di memoria condivisa. Tutto sembra più lavoro di quanto dovrebbe essere strettamente necessario per qualcosa di così elementare. Forse boost :: interprocess :: map non è la scelta giusta per le mie esigenze?

Cosa ho provato? Questo, che compila, ma non ho idea se lo sto facendo bene. In qualche modo mi sento brutta all'interno dereferenziare un ::iterator tornato da find, e poi subito di prendere il suo indirizzo in questo modo:

void ** handle; // actually a parameter in my api. 
*handle = (void*)&(*anAreaMap->find(areaKey)); 

Aggiornamento Le opere di cui sopra. Il consiglio molto ragionevole nella risposta qui sotto non funziona comunque. Usando boost :: interprocess :: string si verifica un errore completo e totale e si verifica un arresto anomalo in fase di runtime. Usando std :: string, che non ha il diritto di funzionare a meno che gli autori di Boost non codificano in particolare il supporto per std :: string, funziona davvero alla grande.

+0

beh, si potrebbe usare a() invece di find(), quindi non ci sarebbe il dereferenziamento :) – EHuhtala

+0

Penso che tu abbia ragione. –

risposta

1

Se handle si suppone essere un puntatore alla std::pair nella memoria condivisa allora il vostro codice funzionerà fornito si sa che areaKey è nella mappa. Non c'è niente di sbagliato in questo, tranne che non è necessario il cast esplicito (e se si esegue il cast allora sarebbe preferibile lo static_cast<void*>()).

Non ho utilizzato boost::interprocess ma penso che sarà necessario utilizzare boost::interprocess::string o un std::basic_string con un allocatore non predefinito per la chiave. A meno che lo boost::interprocess non funzioni sotto il cofano, usando std::string metterà un puntatore alla memoria locale (per il buffer di stringa) nella memoria condivisa che non sarà significativa in un altro processo.

Ecco un programma di test che utilizza una mappa con le chiavi stringa:

#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 è senza argomenti per creare un nuovo segmento di memoria condivisa e con almeno un argomento per aprire un segmento di memoria condivisa esistente (un l'invocazione no-argument deve essere già in esecuzione). In entrambi i casi, il programma leggerà iterativamente una chiave da stdin, inserirà una voce nella mappa e scriverà il contenuto in stdout.

+0

Non ho bisogno di condividere i valori non tipizzati tra i processi, ho semplicemente bisogno di fornire un'API non tipizzata all'interno di un singolo client o server, e utilizzerò internamente le API C++ Boost, che saranno appiattite su Plain Old C per l'interfaccia DLL Obiettivi ABI ('Application binary interface'). –

+0

Capisco che hai solo bisogno di un semplice vecchio 'void *' per il tuo handle. Quello che stavo cercando di dire nel mio secondo paragrafo è che la tua mappa interna non funzionerà correttamente attraverso i processi che la condividono a meno che la sua chiave di stringa sia valida tra i processi, e non sarà valida se è un 'std :: STRING'. Cioè, se hai due processi che cercano di cercare i valori nella mappa, o un processo che inserisce valori e altri valori di lettura, questo probabilmente non funzionerà con un normale 'std :: string' come chiave della mappa. Se non hai bisogno di questa capacità, suppongo di non capire perché la mappa sia condivisa. – rhashimoto

+0

Quindi il tipo di boost boost ('boost :: interprocess :: map') dovrebbe usare 'boost :: interprocess :: map ' giusto? Apprezzo il suggerimento dato che non ci avevo pensato. –