2012-04-05 12 views
3

So arbeite ich an einem einfachen Win32-Wrapper für meine eigene Bequemlichkeit, und ich habe in ein etwas kompliziertes Problem gerannt.Mitgliedsfunktion Zeiger und Vererbung

das hat viele andere Mitglieder, aber ich lasse ein bisschen weg und lasse nur die beleidigenden Mitglieder zurück.

class Windows::AbstractWindow 
{ 
public: 
    void InstallHandler(UINT msgName, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)); 

private: 
    std::map<UINT, void (Windows::AbstractWindow::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex; 

}; 

(für den Datensatz, von Windows in diesem Fall ist ein Namensraum verschiedener Klassen und Objekte die ich gemacht habe)

nur ein bisschen böse, aber lass ich meinen Prozess und Argumentation erklären. Ich habe eine Klasse namens AbstractWindow gemacht, die die Funktionalität eines Fensters auf sehr objektorientierte Weise enthält.

Ich arbeite jetzt an einer Methode, private Mitgliederfunktionen zu nehmen und sie in einer Karte über Zeiger zu ihnen zu speichern, die durch die Windows-Nachricht identifiziert werden, die sie behandeln sollen. Wenn eine Nachricht von der Windows-Prozedur empfangen wird, durchforstet sie diese Map, um zu sehen, ob Sie einen Handler für sie installiert haben. Wenn es das hat ruft es diese Funktion auf und beendet sich. Es hat es nicht DefWindowProc aufgerufen und beendet. leicht genug.

Dieses Objekt soll jedoch niemals instanziiert werden und soll einfach geerbt und erweitert werden. Das Problem besteht darin, dass die Funktion function pointer declaration der Map vom Typ AbstractWindow ist, die es mir nicht erlaubt, Zeiger auf die Mitgliedsfunktion eines Typs zu speichern, der von AbstractWindow geerbt wurde. Zum Beispiel

class BasicWindow : public Windows::AbstractWindow 
{ 
public: 
    BasicWindow() 
    { 
     InstallHandler(WM_CREATE, &create); 
    } 

private: 
    void Create(HWND, UINT, WPARAM, LPARAM) {} 
} 

... ergibt einen Fehler:

error C2664: 'Windows::AbstractWindow::InstallHandler' : cannot convert parameter 2 from 'void (__thiscall BasicWindow::*)(HWND,MSG,WPARAM,LPARAM)' to 'void (__thiscall Windows::AbstractWindow::*)(HWND,UINT,WPARAM,LPARAM)' 

weil die Zeigertypen nicht gleich sind, trotz von der Basisklasse geerbt werden. Will also jemand eine Lösung vorschlagen, während er diese Methode beibehält? Und wenn nicht, bin ich auch offen für Vorschläge, die Ihrer Meinung nach die Handhabung von Nachrichten komfortabler machen würden.

+0

dies ignorieren, danke. –

Antwort

3

Sie auf der Merkwürdiger Recurring Template Pattern lesen sollte (http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern).

Grundsätzlich verwandeln Sie Ihre Basisklasse in eine Vorlage mit einem Vorlagenparameter, der die Unterklasse angibt.

Vielleicht wird es leichter zu verstehen, wenn man es sehen:

namespace Windows 
{ 
    template <typename T> 
    class AbstractWindow 
    { 
    public: 
      void InstallHandler(UINT msgName, void (T::*)(HWND, UINT, WPARAM, LPARAM)); 

    private: 
      std::map<UINT, void (T::*)(HWND, UINT, WPARAM, LPARAM)> HandlerIndex; 

    }; 
} 

class BasicWindow : public Windows::AbstractWindow<BasicWindow> 
{ 
public: 
    BasicWindow() 
    { 
     InstallHandler(WM_CREATE, &BasicWindow::Create); 
    } 

private: 
    void Create(HWND, UINT, WPARAM, LPARAM) {} 
}; 
+0

wow ... das ist wirklich schlau. und wirklich nicht zu viel zu erwarten, dass sich der Endbenutzer des Wrappers daran erinnert, etwas hinzuzufügen. Fügen Sie einfach dieses kleine Template-Argument an der Spitze hinzu und Sie können es gut machen. Yup ich werde damit gehen, danke einen Haufen: P – FatalCatharsis

0

Funktioniert etwas in dieser Art ...? Ps. Ich hatte einen Typedef für die Signatur der Funktion verwendet.

BasicWindow() 
    { 
     InstallHandler(WM_CREATE, reinterpret_cast<void (__thiscall Windows::AbstractWindow::*)(HWND,UINT,WPARAM,LPARAM)>(&create)); 
    } 
+0

Was ist das __thiscall? – FatalCatharsis

+0

und nein, funktioniert nicht. Laufzeitfehler: Run-Time Check Fehler # 0 - Der Wert von ESP wurde nicht ordnungsgemäß über einen Funktionsaufruf gespeichert. Dies ist normalerweise ein Ergebnis des Aufrufs einer Funktion, die mit einer Aufrufkonvention deklariert wurde, mit einem Funktionszeiger, der mit einer anderen Aufrufkonvention deklariert wurde. – FatalCatharsis

3

Das Problem, dem Sie gegenüberstehen, ist, dass Sie versuchen, die Konvertierung in umgekehrter Reihenfolge durchzuführen. Funktionszeiger sind in ihren this Argumenten kontravariant (dh ein Funktionszeiger auf eine Basisklassenfunktion wird für einen Funktionszeiger auf eine abgeleitete Klassenmethode tun, nicht umgekehrt). Sie können:

  • gerade Besetzung (mit static_cast, wie es das Gegenteil einer impliziten Konvertierung ist). Es wird gut funktionieren, solange Sie sicherstellen können, dass Sie nie die Methode für eine ungeeignete Klasse aufrufen (z. B. NotABasicWindow().*method()).
  • loswerden des Schemas und allgemeine Funktionen registrieren (dh. Alles, was anstelle von Mitgliedsfunktionszeiger aufgerufen werden kann). Du würdest zB verwenden. std::function<void(HWND, UINT, WPARAM, LPARAM)> als Handler-Typ und registriert Handler, die ihr Fenster kennen (z. B. Lambda-Funktionen).

    • lambda functions are a feature of C++11. Sie entsprechen ungefähr Funktionsobjekten mit Bindemitteln; Sie erstellen eine anonyme Funktion, die auf Variablen aus dem Bereich verweisen kann, in dem sie sich befinden.Ein Beispiel wäre

      [=](HWND wnd, UINT i, WPARAM wp, LPARAM lp) { this->printHandler(wnd, i, wp, lp); } 
      

      welche this erinnern würde, so würde es printHandler für das aktuelle Objekt (Strom der Code eine Lambda Erstellen nicht Aufruf-Code) bezeichnen, wenn sie aufgerufen. Natürlich könnte das Objekt, auf dem die Methode aufgerufen werden soll, nur ein anderer Parameter sein.

      Lambdas können ebenso wie andere Funktionsobjekte (dh Objekte, die operator() definiert haben) in Objekte std::function konvertiert und dort gespeichert werden.

+0

Whoa so ziemlich alles, was Sie in Ihrem letzten Vorschlag gesagt haben, flog direkt über meinen Kopf. was ist eine reine Funktion, was ist ein Lambda, und wie geht man vor, diese Funktion pro Instanz zu registrieren, ohne dass sie eine Elementfunktion ist? – FatalCatharsis

+0

@FatalCatharsis: Ich habe "reine Funktionen" in "allgemeine Funktionen" umgeschrieben, um Verwechslungen mit dem Begriff der funktionalen Programmierung zu vermeiden. Außerdem habe ich ein paar Links zu Lambdas hinzugefügt. – jpalecek