Boost::ptr_map Problem mit [] Operator



  • Hallo

    Habe in einem Testaufbau eine boost::ptr_map und wollte auf ein Element via [] zugreifen, aber dabei erzeugt er ein neues Objekt (dh. er findet es nicht oder?), was natürlich nicht geht, weil der Basistyp abstrakt ist.
    Also folgendes:

    //...
    		typedef boost::ptr_map< std::string, sk::Building > BuildingCont;
    		typedef BuildingCont::iterator BuildingIter;
    		BuildingCont buildings;
    		std::string Farm1 = "Farm1";
    		std::string Mill1 = "Mill1";
    		buildings.insert( Farm1, building_factory.create_product( sk::FARM ) );
    		buildings.insert( Mill1, building_factory.create_product( sk::MILL ) );
    		buildings[Mill1].print_stocks();
    //...
    

    geht nicht. Aber das hier

    buildings.at(Mill1).print_stocks();
    

    geht. Wundert mich schon, würde lieber den [] Operator verwenden, weil der strong exp.safe ist.
    Darum bitte ich euch mal wieder um hilfreiche Tipps, Links, whatever...
    Danke schonmal.

    ps: Noch die Fehlermeldung (dass die bei Templates auch immer so lang sein müssen...)

    VC8 schrieb:

    c:\...\ptr_map_adapter.hpp(171) : error C2259: 'sk::Building' : cannot instantiate abstract class
    due to following members:
    'sk::Building::~Building(void)' : is abstract
    c:\...\building.h(30) : see declaration of 'sk::Building::~Building'
    'void sk::Building::print_stocks(void)' : is abstract
    c:\...\building.h(31) : see declaration of 'sk::Building::print_stocks'
    'sk::Building::~Building(void)' : is abstract
    c:\...\building.h(30) : see declaration of 'sk::Building::~Building'
    'sk::Building::~Building(void)' : is abstract
    c:\...\building.h(30) : see declaration of 'sk::Building::~Building'
    'void dsg::Observer::update(dsg::Observable *)' : is abstract
    c:\...\observer.h(15) : see declaration of 'dsg::Observer::update'
    c:\...\ptr_map_adapter.hpp(161) : while compiling class template member function
    'sk::Building &boost::ptr_container_detail::ptr_map_adapter_base<T,VoidPtrMap,CloneAllocator>::insert_lookup(const std::basic_string<_Elem,_Traits,_Ax> &)'
    with
    [
    T=sk::Building,
    VoidPtrMap=std::map<std::string,void *,std::lessstd::string,std::allocator<std::pair<const std::string,void *>>>,
    CloneAllocator=boost::heap_clone_allocator,
    _Elem=char,
    _Traits=std::char_traits<char>,
    _Ax=std::allocator<char>
    ]
    c:\...\ptr_map_adapter.hpp(298) : see reference to class template instantiation
    'boost::ptr_container_detail::ptr_map_adapter_base<T,VoidPtrMap,CloneAllocator>' being compiled
    with
    [
    T=sk::Building,
    VoidPtrMap=std::map<std::string,void *,std::lessstd::string,std::allocator<std::pair<const std::string,void *>>>,
    CloneAllocator=boost::heap_clone_allocator
    ]
    c:\...ptr_map.hpp(35) : see reference to class template instantiation 'boost::ptr_map_adapter<T,VoidPtrMap,CloneAllocator>' being compiled
    with
    [
    T=sk::Building,
    VoidPtrMap=std::map<std::string,void *,std::lessstd::string,std::allocator<std::pair<const std::string,void *>>>,
    CloneAllocator=boost::heap_clone_allocator
    ]
    c:\...\main.cpp(19) : see reference to class template instantiation 'boost::ptr_map<Key,T>' being compiled
    with
    [
    Key=std::string,
    T=sk::Building
    ]



  • sry fürs *push*



  • Gehts so?

    //...
    		typedef boost::ptr_map< std::string, sk::Building > BuildingCont;
    		typedef BuildingCont::iterator BuildingIter;
    		BuildingCont buildings;
    		std::string Farm1 = "Farm1";
    		std::string Mill1 = "Mill1";
    		buildings.insert( BuildingCont::value_type( Farm1, building_factory.create_product( sk::FARM ) ) );
    		buildings.insert( BuildingCont::value_type( Mill1, building_factory.create_product( sk::MILL ) ) );
    		buildings[Mill1].print_stocks();
    //...
    


  • Leider nicht.

    buildings.insert( BuildingCont::value_type( Farm1, building_factory.create_product( sk::FARM ) ) );
    		buildings.insert( BuildingCont::value_type( Mill1, building_factory.create_product( sk::MILL ) ) );
    

    ergibt

    c:\...\main.cpp(24) : error C2564: 'sk::Building *' : a function-style conversion to a built-in type can only take one argument
    c:\...\main.cpp(24) : error C2660: 'boost::ptr_map_adapter<T,VoidPtrMap,CloneAllocator>::insert' : function does not take 1 arguments

    Die Boost::ptr_map nimmt Key und Value nur einzeln, nicht als Pair, laut Docu.

    Also hab ich folgendes probiert:

    buildings.insert( Farm1, BuildingCont::value_type( building_factory.create_product( sk::FARM ) ) );
    		buildings.insert( Mill1, BuildingCont::value_type( building_factory.create_product( sk::MILL ) ) );
    

    Das compiliert er, aber der Aufruf

    buildings[Mill1].print_stocks();
    

    produziert den selben Fehler wie oben. Mit .at(Mill1) gehts wieder.
    Das Problem ist wohl, das die Factory einen Zeiger auf ein von Building abgeleitetes Objekt zurückgibt, der [] Operator aber zu strikt ist.
    Ich dachte ptr_container wären für polymorphe Objekte gut geeignet?



  • Ooptimist schrieb:

    Leider nicht.

    buildings.insert( BuildingCont::value_type( Farm1, building_factory.create_product( sk::FARM ) ) );
    		buildings.insert( BuildingCont::value_type( Mill1, building_factory.create_product( sk::MILL ) ) );
    

    ergibt

    c:\...\main.cpp(24) : error C2564: 'sk::Building *' : a function-style conversion to a built-in type can only take one argument
    c:\...\main.cpp(24) : error C2660: 'boost::ptr_map_adapter<T,VoidPtrMap,CloneAllocator>::insert' : function does not take 1 arguments

    Die Boost::ptr_map nimmt Key und Value nur einzeln, nicht als Pair, laut Docu.

    Tschuldigung, ich hatte bei std::map geschaut 🙄

    Ooptimist schrieb:

    Mit .at(Mill1) gehts wieder.

    Dann nimm doch das. Die Ausnahme, die geworfen wird hat ja durchaus ihre Daseinsberechtigung.

    Ooptimist schrieb:

    Das Problem ist wohl, das die Factory einen Zeiger auf ein von Building abgeleitetes Objekt zurückgibt, der [] Operator aber zu strikt ist.

    Kaum.

    Ooptimist schrieb:

    Ich dachte ptr_container wären für polymorphe Objekte gut geeignet?

    Dafür sind sie gemacht.



  • Ok danke. Werd wohl damit leben müssen.

    Versuch grad rauszukriegen wieso..
    Folgendes aus der Boost ptr_map_allocator.hpp:

    reference at( const key_type& key )                                              
            {                   
                return lookup( key );                                                         
            }
    
            reference operator[]( const key_type& key )                                              
            {                          
                return insert_lookup( key );
            }
    
    reference lookup( const key_type& key ) const
            {
               iterator i = const_cast<ptr_map_adapter_base*>(this)
                                              ->find( key );
               if( i != const_cast<ptr_map_adapter_base*>(this)->end() )
                   return *i;
               else                                           
                   throw bad_ptr_container_operation( "'ptr_map/multimap::at()' could"
                                                        " not find key" );
            }
    
            reference insert_lookup( const key_type& key )
            {
                void*& ref = this->c_private()[key];
                if( ref )
                {
                    value_type v = static_cast<value_type>( ref );
                    return *v;
                }
                else
                {
                    eraser e(&this->c_private(),key); // nothrow
                    value_type res = new T();         // strong   // Hier hakt es
                    ref = res;                        // nothrow
                    e.release();                      // nothrow
                    return *res;
                }
              }
    

    Also ich versteh das so, wenn er bei insert_lookup den Key nicht findet, erstellt er einen neuen value_type, was nicht geht, weil der abstract ist. (Was this->c_private() tut hab ich nicht rausgefunden) Lookup hingegen schmeisst lediglich eine Exception.
    Da der Key aber existiert, kommt es gar nicht dazu, der Compiler motzt aber trotzdem.
    Mit diesem Verhalten eignet sich der Operator[] nicht, wenn die Basisklasse abstrakt ist. "to strong for me"
    Punkt und wieder was gelernt. 🙂



  • Sry fürs Doppelpost..

    @.filmor Du hattest mir doch den ptr_vector empfohlen für die Gebäude. Ich hab nun deshalb die ptr_map genommen, weil ich die Gebäude zum Teil direkt ansprechen muss bei "Namen" (also Farm1 Farm2 etc) zum Verknüpfen der Pfade und ev auch zum Löschen.
    Ich dachte ist ev. effizienter so, als den Objekten einen Member zu geben und den Vector danach zu durchsuchen, oder seh ich das falsch?



  • Hmm, die Map ist imho recht ungeeignet. Allein schon die Strings, die du reinschreibst: "Farm1", "Mill1", ziemlich redundant das Ganze. Wann genau musst du die denn "mit Namen" ansprechen? Ich finde den Vektor immernoch am geeignetsten, wobei ein multiset wohl richtiger wäre.
    Mit der Map ist es mit ziemlicher Sicherheit nicht effizienter. Da du ja scheinbar sowieso ein enum verwendest, um deine Fabrik zu betreiben, könntest du auch nach diesem Wert sortieren.

    /btw, du könntest dich mal anmelden 😉



  • .filmor schrieb:

    Hmm, die Map ist imho recht ungeeignet. Allein schon die Strings, die du reinschreibst: "Farm1", "Mill1", ziemlich redundant das Ganze. Wann genau musst du die denn "mit Namen" ansprechen?

    Mit Namen ansprechen muss ich vor allem beim erstellen der Verknüpfung der Wege und registrieren der Observer-Beziehungen. Im Moment hab ich das zum testen etwa so

    sk::Road r_farm1_mill1( 5 );  //5 ist die Distanz
    		buildings.at(Farm1).add_observer( &r_farm1_mill1 );
    		r_farm1_mill1.add_observer( &buildings.at(Mill1) );
    //Soll dann mal in ne Funktion
            road_net.build_road( Farm1, Mill1 );
            road_net.build_road( Farm2, Mill1 );
    

    Ich hab ja dann mehrere Farmen und Mühlen, und welcher Weg dann wohin führt muss ich ja irgendwie festlegen.
    Für elegante Designertipps wär ich natürlich sehr offen 😉

    .filmor schrieb:

    Ich finde den Vektor immernoch am geeignetsten, wobei ein multiset wohl richtiger wäre.
    Mit der Map ist es mit ziemlicher Sicherheit nicht effizienter. Da du ja scheinbar sowieso ein enum verwendest, um deine Fabrik zu betreiben, könntest du auch nach diesem Wert sortieren.

    Könnt ich dann im Multiset nur alle Mühlen durchiterieren, oder wie meinst du das jetzt?

    .filmor schrieb:

    /btw, du könntest dich mal anmelden 😉

    Trau mich aber nicht..nOops werden hier nicht immer nett behandelt.



  • Ooptimist schrieb:

    .filmor schrieb:

    Hmm, die Map ist imho recht ungeeignet. Allein schon die Strings, die du reinschreibst: "Farm1", "Mill1", ziemlich redundant das Ganze. Wann genau musst du die denn "mit Namen" ansprechen?

    Mit Namen ansprechen muss ich vor allem beim erstellen der Verknüpfung der Wege und registrieren der Observer-Beziehungen. Im Moment hab ich das zum testen etwa so

    sk::Road r_farm1_mill1( 5 );  //5 ist die Distanz
    		buildings.at(Farm1).add_observer( &r_farm1_mill1 );
    		r_farm1_mill1.add_observer( &buildings.at(Mill1) );
    //Soll dann mal in ne Funktion
            road_net.build_road( Farm1, Mill1 );
            road_net.build_road( Farm2, Mill1 );
    

    Ich hab ja dann mehrere Farmen und Mühlen, und welcher Weg dann wohin führt muss ich ja irgendwie festlegen.
    Für elegante Designertipps wär ich natürlich sehr offen 😉

    Eigentlich sind Wege ja immer zwischen zwei Fahnen (und glaube mir, sobald du Graphik hast ist das deutlich einfacher, als das Siedler-III-Prinzip). Für die Wegfindung sorgst du dann mit Graphenalgorithmen (siehe Boost Graph Library) (auch wenns kompliziert klingt, du brauchst eigentlich nicht alles darüber zu wissen, es reicht vollkommen, wenn du einen der Algorithmen zum kürzesten Weg benutzt). Dein Straßennetz ist dann einfach ein ungerichteter Graph und build_road ist insert (fahne1, fahne1) oder so ähnlich.
    Jedes Gebäude hat eine spezielle Fahne, die es beobachtet.

    Ooptimist schrieb:

    .filmor schrieb:

    Ich finde den Vektor immernoch am geeignetsten, wobei ein multiset wohl richtiger wäre.
    Mit der Map ist es mit ziemlicher Sicherheit nicht effizienter. Da du ja scheinbar sowieso ein enum verwendest, um deine Fabrik zu betreiben, könntest du auch nach diesem Wert sortieren.

    Könnt ich dann im Multiset nur alle Mühlen durchiterieren, oder wie meinst du das jetzt?

    Jo ;). Du kannst auch die (von der Entfernung) nächste Mühle oder die mit der geringsten Auslastung etc. suchen.

    Ooptimist schrieb:

    .filmor schrieb:

    /btw, du könntest dich mal anmelden 😉

    Trau mich aber nicht..nOops werden hier nicht immer nett behandelt.

    Unsinn. Nur wenn sie dumme Fragen stellen. Aber dumm waren deine Bisherigen nicht 👍. Außerdem macht es praktisch keinen Unterschied, ob nun ein Unregistrierter namens Ooptimist oder ein Registrierter auftritt, bis darauf, dass man sich bei letzterem sicher ist.



  • Danke.

    Wieder etwas zur Lektüre 🙂 Werds mir mal durchlesen, muss nun aber mal ne Pause machen, frische Luft, essen und so..

    Also damit ichs richtig versteh, mal ohne die Wege Graphen.
    Die Gebäude pack ich weiterhin in nen ptr_vector und lasse sie gleich bei Konstruktion ihre eigene Fahne beobachten. Verschiebt sich dadurch mein Problem nicht auf die Wiederfindung der richtigen Fahne?



  • Da jedes Gebäude genau eine Fahne hat und diese sich nie in irgendeiner Weise verändert, kann das Gebäude eine Referenz darauf halten, die dann z.B. mit building.flag () zurückgegeben wird.
    Das Problem bleibt dasselbe ;).
    Bei Siedler ist es ja so, dass die Träger den Transport übernehmen. Du musst einfach nur für jeden Träger schauen, was er als nächstes von Fahne 1 nach Fahne 2 bringen muss und umgekehrt. Den genauen Weg kannst du gar nicht kennen, weil zum Beispiel die Fahne schon voll ausgelastet sein kann.


Log in to reply