pointer in hash_multimap speichern



  • Ich bin dabei einen Callbackmanager zu implementieren, Dazu möchte ich beliebige Methoden (mit definiertem Methoden-header) aus beliebigen Klassen entgegennehmen und in einer hash_multimap mit einem unsigned int gepaart abspeichern. Der unsigned int soll dabei mein key sein und reflektiert einen Tastaturkey. (Ich möchte auf Keyboardtastendrücke callbacks ausführen)
    Dazu habe ich mir eine Callbackclasse angelegt, die auf einem Template basiert, damit ich den Klassentyp der Callbackmethode nicht kennen muss. In dieser Klasse wird dann auch der Methodenzeiger entgegengenommen und abgespeichert.
    Mein Callbackmanager nimmt dann einen pointer auf eine Instanz einer solchen Callbackclasse entgegen und speichert ihn mit einem unsigned int in meine hash_multimap.
    Das Problem:
    Wenn ich die Instanz der Klasse Callback direkt nutze um den gespeicherten Methodenpointer zu nutzen klappt alles wunderbar, aber wenn ich den pointer auf die Instanz in die hash_multimap packe, auslese und dann versuche die Methode auszuführen bekomme ich eine exception.
    Auch wenn ich versuche Membervariabeln aus meiner Callbackinstanz zu lesen, nachdem ich den pointer aus der map geholt habe bekomme ich irgendwelche wilden Werte raus. Also denke ich mal, dass der pointer hinüber ist.
    Hier der relevante source zum Speichern der Callbackklasse in der hash_multimap.

    WWRCallbackManager_cl.h:

    ... 
    stdext::hash_multimap<unsigned int,WWRCallback_cl*>  callbacktable; 
    void RegisterCallback(unsigned int, WWRCallback_cl*); 
    ...
    

    WWRCallbackManager_cl.cpp:

    void WWRCallbackManager_cl::RegisterCallback(unsigned int key, WWRCallback_cl* cbobject) 
    { 
       callbacktable.insert(stdext::hash_multimap<unsigned int,WWRCallback_cl*>::value_type(key,cbobject)); 
    } 
    
    void WWRCallbackManager_cl::TickFunction() 
    { 
       stdext::hash_multimap <unsigned int, WWRCallback_cl*>::iterator cb_Iter; 
       if(callbacktable.size()>0) 
       { 
          for ( cb_Iter = callbacktable.begin( ); cb_Iter != callbacktable.end( );cb_Iter++ ) 
          { 
             if(Vision::Key.IsPressed(cb_Iter -> first)) 
             { 
    
                cb_Iter -> second -> Execute(3);   //Exception tritt hier auf 
             } 
          } 
       } 
    }
    

    Was genau mach ich falsch?
    Danke schonmal für alle Hilfe.
    -Jan-



  • Da ist es interessant zu wissen, wo und wie du deine Callback's anlegst und registrierst. Wenn du einfach nur die Adresse einer lokalen Variable übergibst, hast du sehr schnell ein Problem ('normale' Containerklassen speichern zwar eine Kopie ihrer Elemente, aber bei Pointern ist das eine flache Kopie):

    void init()
    {
      WWRCallback_cl Call;
      ...
      RegisterCallback(4711,&Call);
      ...
    }//hier wird 'Call' wieder gelöscht und deine Hash-Map behält einen Zeiger, der ins Nirvana verweist
    // -> BUMM!!
    


  • Hallo CStoll!
    Danke für deine Antwort.
    Also ich hab mal was ausprobiert:
    Wenn ich direkt in der Methode, wo ich das Objekt vom Typ Callback in die hashmap packe auch wieder danach suche, dann klappt das wunderbar.
    Die Klasse, die mein Hash-Objekt entgegennimmt registriert sich allerdings in der Gameengine um jeden Frame aufgerufen zu werden. Wenn ich dann in der Methode, die jeden Frame aufgerufen wird wieder in der hash_multimap alle Elemente durchgehe sind die pointer wohl falsch.
    Also denke ich mal, dass du mit deinem Denkansatz nicht ganz falsch liegst.

    Hier erstelle ich meine Callbacks:

    Gameapp.cpp

    //register callbacks
       callbackmanager=new WWRCallbackManager_cl(); // create new cb manager
       callbackmanager->RegisterMovementControl("wasd",VGLK_A,VGLK_D,VGLK_W,VGLK_S);
       TWWRCallback_cl<MyLandspeeder_cl> testcallback; // create new cb instance
       testcallback.SetCallback(pLandspeeder,&MyLandspeeder_cl::keycall); //register the cb method in the cb instance
       callbackmanager->RegisterCallback(VGLK_SPACE,&testcallback); //register the callback instance with the callback manager for spacebar
    

    Wie genau verhindere ich jetzt, dass mein Objektzeiger ins Nirvana zeigt?
    muss ich dazu einfach den callback als Membervariable von Gameapp deklarieren?
    Das wäre unangenehm, denn gerade wegen der Dynamik wollte ich ja den Callbackmanager haben.
    Oder darf ich das einfach nicht über Pointer machen?

    Vielen dank nochmals.
    Gruß
    Jan



  • KungFool schrieb:

    TWWRCallback_cl<MyLandspeeder_cl> testcallback; // create new cb instance
    testcallback.SetCallback(pLandspeeder,&MyLandspeeder_cl::keycall); //register the cb method in the cb instance
    callbackmanager->RegisterCallback(VGLK_SPACE,&testcallback); //register the callback instance with the callback manager for spacebar
    

    Genau wie ich vermutet habe - du erzeugst ein lokales Callback-Objekt und das wird am Ende der Funktion wieder zerstört.

    Wie genau verhindere ich jetzt, dass mein Objektzeiger ins Nirvana zeigt?
    muss ich dazu einfach den callback als Membervariable von Gameapp deklarieren?
    Das wäre unangenehm, denn gerade wegen der Dynamik wollte ich ja den Callbackmanager haben.
    Oder darf ich das einfach nicht über Pointer machen?

    Du darfst das schon über Pointer machen - nur mußt du darauf achten, woher du deine Pointer bekommst (Stack-Speicher (lokale Variablen) wird am Funktionsende freigegeben, Heap-Speicher bleibt gültig, bis du ihn selber freigibst (new/delete):

    TWWRCallback_cl<MyLandspeeder_cl>* ptest = new TWWRCallback_cl<MyLandspeeder_cl>; // create new cb instance
    ptest->SetCallback(pLandspeeder,&MyLandspeeder_cl::keycall); //register the cb method in the cb instance
    callbackmanager->RegisterCallback(VGLK_SPACE,ptest); //register the callback instance with the callback manager for spacebar
    


  • Hmm ja ich hab das einfach nicht richtig instanziiert und den Pointer benutzt.
    So ist das natürlich genau das was ich brauche und es klappt auch.
    Herzlichen Dank noch einmal!
    Jan


Log in to reply