2013-03-24 4 views
7

Ich bin ein wenig verwirrt durch die Semantik Heap und By-Value-versus-by-Referenz beteiligt einen std::string Schlüssel und einen großen struct Wert in einen Container wie boost::interprocess::map.Wie kann ich ein undurchsichtiges Handle (void * oder dword) zurückgeben, das zu einem Wertelement zurückgeworfen werden kann, das in einer boost :: interprocess map gespeichert ist?

Hier ist meine Situation, und einige typedefs Ich verwende:

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; 

Hier ist, wie ich das Einfügen AreaValueType (das ist ein typedef für std :: pair):

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

Ich glaube, der obige Code kopiert A, das ist ein std :: -Paar auf meinem lokalen Stapel (nicht gemeinsam genutzten Speicher) in einen gemeinsamen Speicherbereich. Kann ich den shared memory Bereich innerhalb der boost :: interprocess :: map Handle bekommen oder bin ich darauf beschränkt diesen Datensatz zurück zu holen und ihn ganz zu speichern? (Mit anderen Worten, kann ich etwas wie eine Struktur in eine Boost-Interprozess-Map speichern und dann ein einzelnes Byte innerhalb dieses Datensatzes aktualisieren, oder muss ich nur den gesamten Datensatz aktualisieren, indem ich alle Bytes in einer DATA_AREA_DESC-Struktur durch völlig neue ersetzt habe bytes)

Einige weitere Klärung.

  1. ich habe eine einfache alte ANSI-C-DLL Export api, die C++ intern verwendet, und boost :: inter :: map. Es wird erwartet, dass die Funktion ein Element in der Map erstellt und dann ein Handle zurückgibt. Wie kann ich etwas in die boost :: interprocess :: map einfügen und dann ein Handle an diese Entität an Nicht-C++ - Benutzer übergeben, vorzugsweise an void* oder unsigned long? Alles, was ich tun kann, ist, Sachen aus dem gemeinsamen Speicher zu holen, indem ich den Schlüsselwert std :: string nachschaue und einen neuen Datensatz in den Speicher schreibe. Ich möchte stattdessen in der Lage sein, einen Verweis auf das Shared Memory-Objekt zu behalten.

  2. Wenn ich das nicht direkt tun kann, wie würde ich es indirekt tun? Ich nehme an, ich könnte ein non-shared-memory std :: vector behalten und einen nicht-shared memory std :: string zuweisen, der den Wert von areaKey enthält, was eine std :: string ist, und dann eine Umwandlung von void* Element zurück auf std::string und dann verwenden, um einen Datensatz aus dem gemeinsamen Speicherbereich zu holen. Das alles scheint mehr Arbeit zu sein als unbedingt notwendig für etwas so elementares. Vielleicht ist boost :: interprocess :: map nicht die richtige Wahl für meine Anforderungen?

Was habe ich versucht? Das kompiliert, aber ich habe keine Ahnung, ob ich das richtig mache. Irgendwie fühle ich mich hässlich innen dereferencing ein ::iterator von find zurückgekehrt, und dann sofort unter seiner Adresse in etwa so:

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

aktualisieren Die oben genannten Arbeiten. Der sehr vernünftige Rat in der Antwort unten funktioniert jedoch nicht. Die Verwendung von boost :: interprocess :: string führt zu einem vollständigen und vollständigen Fehler und stürzt zur Laufzeit ab. Mit std :: string, das kein Recht auf Arbeit hat, außer die Autoren von Boost codierten std :: string Unterstützung vor allem, funktioniert wirklich gut.

+0

gut, könnten Sie at() anstelle von find() verwenden, dann gäbe es keine Dereferenzierung :) – EHuhtala

+0

Ich denke, Sie haben Recht. –

Antwort

1

Wenn handle soll ein Zeiger auf die std::pair im gemeinsam genutzten Speicher sein, dann wird Ihr Code arbeiten vorausgesetzt, Sie wissen, dass areaKey in der Karte ist. Es ist nichts falsch daran, außer dass du die explizite Besetzung nicht brauchst (und wenn du Cast machst, wäre static_cast<void*>() vorzuziehen).

Ich habe boost::interprocess nicht verwendet, aber ich denke, Sie müssen oder std::basic_string mit einem nicht standardmäßigen Zuordner für Ihren Schlüssel verwenden. Es sei denn, boost::interprocess tut etwas Phantasie unter der Haube, mit std::string wird ein Zeiger auf den lokalen Speicher (für den String-Puffer) in den gemeinsamen Speicher, die in einem anderen Prozess nicht sinnvoll sein.

Hier ist ein Testprogramm, das eine Karte mit String-Schlüsseln verwendet:

#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 es ohne Argumente ein neues Shared-Memory-Segment zu erstellen und mit mindestens einem Argumente ein vorhandenes Shared-Memory-Segment zu öffnen (a No-Argument-Aufruf muss bereits ausgeführt werden). In beiden Fällen liest das Programm iterativ einen Schlüssel aus stdin, fügt einen Eintrag in die Karte ein und schreibt den Inhalt in stdout.

+0

Ich muss die untypped Werte über Prozesse nicht teilen, ich muss einfach eine untypisierte API innerhalb eines einzelnen Klienten oder Servers zur Verfügung stellen und intern die Boost C++ APIs benutzen, die zu Plain Old C für DLL Schnittstelle abgeflacht werden ABI ('Application binary interface') Zwecke. –

+0

Ich verstehe, dass Sie nur eine einfache alte 'void *' für Ihr Handle benötigen. Was ich in meinem zweiten Absatz sagen wollte, ist, dass Ihre interne Map nicht korrekt über die Prozesse funktioniert, die sie teilen, es sei denn, ihr String-Schlüssel ist über Prozesse hinweg gültig, und sie ist nicht gültig, wenn es ein 'std :: Zeichenfolge ". Das heißt, wenn Sie zwei Prozesse haben, die versuchen, Werte in der Map nachzuschlagen, oder einen Prozess, der Werte und andere Lesewerte einfügt, funktioniert dies wahrscheinlich nicht mit einem gewöhnlichen 'std :: string' als Map-Schlüssel. Wenn Sie diese Fähigkeit nicht brauchen, dann verstehe ich nicht, warum die Karte geteilt wird. – rhashimoto

+0

Also sollte der Boost-MAP-Typ ('boost :: interprocess :: map')' boost :: interprocess :: map 'richtig? Ich schätze den Tipp, da ich nicht daran gedacht hatte. –

Verwandte Themen