2009-08-17 11 views
0

Ich habe ein Projekt mit einer großen Codebasis (> 200.000 Zeilen Code) ich pflegen ("The Core").Überschreiben/Ändern von C++ - Klassen mit DLLs

Derzeit verfügt dieser Kern über eine Skript-Engine, die aus Hooks und einer Skript-Manager-Klasse besteht, die alle süchtig Funktionen (die per DLL registriert) aufruft, wenn sie auftreten. Um ganz ehrlich zu sein, ich weiß nicht, wie genau das funktioniert, da der Kern meistens undokumentiert ist und mehrere Jahre und eine Größenordnung von Entwicklern (die natürlich abwesend sind) überspannt. Ein Beispiel für die aktuelle Scripting-Engine ist:

void OnMapLoad(uint32 MapID) 
{ 
    if (MapID == 1234) 
    { 
     printf("Map 1234 has been loaded"); 
    } 
} 

void SetupOnMapLoad(ScriptMgr *mgr) 
{ 
    mgr->register_hook(HOOK_ON_MAP_LOAD, (void*)&OnMapLoad); 
} 

Eine zusätzliche Datei setup.cpp ruft SetupOnMapLoad mit dem Kern des genannten ScriptMgr.

Diese Methode ist nicht was ich suche. Für mich ist die perfekte Skript-Engine eine, mit der ich die Hauptklassenmethoden überschreiben kann. Ich möchte in die Lage, Klassen zu erstellen, die von Core-Klassen erben und erweitert auf sich, wie so:

// In the core:  
class Map 
{ 
    uint32 m_mapid; 
    void Load(); 
    //... 
} 

// In the script: 
class ExtendedMap : Map 
{ 
    void Load() 
    { 
     if (m_mapid == 1234) 
      printf("Map 1234 has been loaded"); 

     Map::Load(); 
    } 
} 

Und dann will ich jede Instanz Map sowohl im Kern und Skripte, um tatsächlich eine Instanz von ExtendedMap sein.

Ist das möglich? Wie?

Antwort

2

Die Vererbung ist möglich. Ich sehe keine Lösung, um die Instanzen von Map durch Instanzen von ExtendedMap zu ersetzen.

Normalerweise könnten Sie das tun, wenn Sie eine Factory-Klasse oder -Funktion hätten, die immer zum Erstellen eines Map-Objekts verwendet wird, aber dies ist eine Frage des vorhandenen (oder nicht vorhandenen) Designs.

Die einzige Lösung, die ich sehe, ist in den Code für Instanziierungen suchen und versuchen, sie von Hand zu ersetzen. Dies ist riskant, weil Sie einige davon übersehen könnten, und es könnte sein, dass einige der Instanziierungen nicht im Quellcode enthalten sind (z. B. in dieser alten DLL).

Später bearbeiten Diese Methode überschreiben hat auch eine Nebenwirkung im Falle der Verwendung in einer polymorphen Art und Weise.

Beispiel:

Map* pMyMap = new ExtendedMap; 

pMyMap->Load(); // This will call Map::Load, and not ExtendedMap::Load. 
+0

Ich möchte nicht, dass die Quelle "ExtendedMap" hat, wenn die Skripte keine solche Klasse bereitstellen. Die Idee ist, bestimmte (oder alle) Klassen neu zu registrieren, nachdem ich sie erweitert habe, in der DLL selbst. – MoshiBin

2

Das klingt wie ein Lehrbuch für das "Decorator" Design-Muster.

+0

Sounds in der Nähe von was ich suche, aber wie verursache ich den Kern immer den Dekorator von Skripten und Fallback verwenden zu der Basisklasse (oder einem Dekorator davon)? Ich möchte, dass mein Kern immer 'MapDecorator' verwendet, der in einer externen DLL definiert werden könnte. – MoshiBin

+0

Ich denke, Sie müssten den Core-Code neu gestalten, so dass die gesamte Erstellung von Map-Objekten über eine Factory erfolgt Machen Sie eine Oberfläche sichtbar, die Ihre Skript-Hooks verwenden könnten, um zu steuern, welche Art von dekorierter Map erstellt wird. Ich mag einige der Ideen, die ich in den anderen Antworten gesehen habe; vielleicht passt einer von ihnen besser zu Ihrer Situation. –

0

Obwohl es möglich ist, ist es ziemlich gefährlich: Das System sollte zur Erweiterung offen sein (d. H. Haken), aber geschlossen für Änderungen (d. H. Überschreiben/neu definieren). Wenn Sie so erben, können Sie das Verhalten Ihres Client-Codes nicht vorhersehen. Wie Sie in Ihrem Beispiel sehen, muss der Client-Code daran erinnern, die Methode der Superklasse aufzurufen, was nicht geschieht :)

Eine Option wäre, eine nicht-virtuelle Schnittstelle zu erstellen: eine abstrakte Basisklasse, die einige Template-Methoden hat dass Aufruf rein virtuelle Funktionen. Diese müssen von Unterklassen definiert werden.

Wenn keine Kern-Maps erstellt werden sollen, sollte das Skript dem Kern eine Factory zum Erstellen von Map-Nachkommen geben.

0

Wenn meine Erfahrung mit ähnlichen Systemen auf Ihre Situation anwendbar ist, werden mehrere Haken registriert.Eine Lösung basierend auf dem Muster abstrakte Fabrik wird nicht wirklich funktionieren. Ihr System ist in der Nähe des Musters Beobachter, und das ist, was ich verwenden würde. Sie erstellen eine Basisklasse mit allen möglichen Hooks als virtuelle Member (oder mehrere mit zugehörigen Hooks, wenn die Hooks zahlreich sind). Anstatt die Hooks nacheinander zu registrieren, registrieren Sie ein Objekt eines Typs, der von der Klasse abgeleitet ist, mit der erforderlichen Überschreibung. Das Objekt kann einen Zustand haben und in vorteilhafter Weise die void* user data Felder ersetzen, die solche Rückrufsysteme gemeinsam haben.

Verwandte Themen