boost::interprocess Container - T hat keinen T() Konstruktor?



  • Huhu,

    Ich habe heute Nacht ein bisschen mit boost::interprocess herumgespielt. Ich möchte im Prinzip eine map<uint32_t, string> in einem Shared Memory haben. Allerdings bekomme ich ewig lange Compiler-Fehlermeldungen, bei denen ich mir nicht sicher bin, sie richtig gedeutet zu haben.

    Code:

    typedef boost::interprocess::managed_shared_memory::segment_manager manager;
    
    			typedef boost::interprocess::allocator<char, manager> char_allocator;
    
    			typedef std::uint32_t key_type;
    			typedef boost::interprocess::basic_string<char, std::char_traits<char>, char_allocator> value_type;
    
    			typedef std::pair<key_type, value_type> pair_type;
    			typedef boost::interprocess::allocator<pair_type, manager> pair_allocator;
    
    			typedef boost::interprocess::map<key_type, value_type, std::less<pair_type>, pair_allocator> map_type;
    
    			boost::interprocess::managed_shared_memory mem;
    			map_type entries;
    
    			static constexpr char const* shared_memory_name = "paprikachu::identd::port_resolver";
    			enum { shared_memory_size = 1024 };
    
    		public:
    			port_resolver()
    				: mem(boost::interprocess::create_only, shared_memory_name, shared_memory_size)
    				, entries(std::less<key_type>(), pair_allocator(mem.get_segment_manager()))
    			{}
    

    Der Compiler meckert nun im Konstruktor rum. Da die Fehlermeldung etwa lang ist, habe ich sie auf ideone gepastet: Klick

    Sollte meine Vermutung richtig sein, so liegt das Problem darin, dass der String nicht default-Konstruierbar ist. Das ist auch logisch, denn der String will einen Allokator im Konstruktor, und dieser hat ebenfalls keinen T() Konstruktor.

    1.) Liege ich mit dieser Vermutung richtig?
    2.) Falls ja, hat jemand einen Lösungsvorschlag für dieses Problem?

    Ich habe almählich das Gefühl, dass sich boost::interprocess nicht wirklich für irgendwas eignet. Oder ich mache irgendwas grundsätzlich falsch.

    Grüße,
    PI



  • 1.) Liege ich mit dieser Vermutung richtig?

    Vielleicht, erstmal solltest du den ersten auftretenden Fehler beheben, der hat nichts mit dem String zu tun.

    Schau dir in den ganzen Fehlermeldungen mal an, was das Templateargument Pred ist, und vergleiche das mit dem, was du dem Map-Ctor als Parameter gibst. Und dann überlege, welches von beiden richtig ist. (Du hast die Map mit einem Prädikat definiert und übergibst ein anderes an den Ctor, das nicht kompatibel ist)



  • Hm, jetzt kompilierts. Trotzdem würde mich interessieren, warum das funktioniert, obwohl der Allokator keinen Default-Konstruktor hat.



  • Ich seh in deinem Code nirgendwo, dass du einen default-konstruierten String in die Map stopfst, weder explizit noch implizit (aufruf op[] oder ähnliches).
    Die Map ist ein Template, und jede einzelne Methode ist ein Funktionstemplate. Heißt, wenn du keine Methode aufrufst, die eine default-Konstruktion eines value_type vornimmt, wird der entsprechende Code nie übersetzt und du kommst ohne den default-Ctor aus.



  • Die Klasse wird aber instanziert, nur eingefügt wird im Moment noch nichts. Die map alloziert doch bestimmt im Voraus speicher für zukünftige Objekte, dafür brauchte sie ja einen default Ctor, oder?



  • Nachtrag: ich hab in der Boost Doku mal rumgesucht, und es sieht für mich so aus, als ob boost::interprocess::basic_string::basic_string(allocator) einen default-Parameter hat:

    explicit basic_string(const allocator_type& a = allocator_type())
    

    Genau wie std::basic_string. (Hätte mich auch gewundert, wenn nicht)



  • 314159265358979 schrieb:

    Die Klasse wird aber instanziert, nur eingefügt wird im Moment noch nichts.

    Wenn die Klasse instanziert wird, werden noch lange keine Methoden instanziert. Nochmal: Es wird nur instanziert, was auch benutzt wird.

    Die map alloziert doch bestimmt im Voraus speicher für zukünftige Objekte, dafür brauchte sie ja einen default Ctor, oder?

    Nein. Speicherallokation ist nicht Objektkonstruktion. Der Speicher wird roh alloziert (vom Allocator), und erst wenn Objekte eingefügt werden, wird per Placement new ein Objekt in den rohen Speicher konstruiert. Und wenn du für dieses Einfügen Objekte von draußen reingibst, wird Copy/Move-konstruiert.

    Zur Überprüfung:

    #include <vector>
    #include <iostream>
    struct RefCnt
    {
      static unsigned cnt;
    
      RefCnt() { ++cnt; }
      RefCnt(RefCnt const&) { ++cnt; }
      ~RefCnt() { --cnt; }
    };
    
    unsigned RefCnt::cnt = 0;
    
    int main()
    {
      std::vector<RefCnt> rcv;
      rcv.reserve(1000); //Speicher wird alloziert für 1000 RefCnt, aber keiner konstruiert
      std::cout << RefCnt::cnt << '\n';
    
      rcv.resize(100);
      std::cout << RefCnt::cnt << '\n';
    
      rcv.resize(0);
      std::cout << RefCnt::cnt << '\n';
    }
    


  • pumuckl schrieb:

    Nachtrag: ich hab in der Boost Doku mal rumgesucht, und es sieht für mich so aus, als ob boost::interprocess::basic_string::basic_string(allocator) einen default-Parameter hat:

    explicit basic_string(const allocator_type& a = allocator_type())
    

    Genau wie std::basic_string. (Hätte mich auch gewundert, wenn nicht)

    Mein Allokator hat aber keinen Default-Ctor. Er benötigt als Parameter den segment_manager des Shared Memory.

    pumuckl schrieb:

    Wenn die Klasse instanziert wird, werden noch lange keine Methoden instanziert. Nochmal: Es wird nur instanziert, was auch benutzt wird.

    Stimmt, daran habe ich gerade nicht gedacht.

    Nein. Speicherallokation ist nicht Objektkonstruktion. Der Speicher wird roh alloziert (vom Allocator), und erst wenn Objekte eingefügt werden, wird per Placement new ein Objekt in den rohen Speicher konstruiert. Und wenn du für dieses Einfügen Objekte von draußen reingibst, wird Copy/Move-konstruiert.

    Okay, auch irgendwie logisch. Ich hatte da irgendwas in Erinnerung, aber das war wohl Blödsinn. Danke.


Anmelden zum Antworten