2017-11-28 4 views
3

Ich habe oft Gebrauch von Funktionszeigern bei der Arbeit mit Szenarios vom Typ "Befehl und Steuerung" gemacht, wo eine Nachricht an einen Prozess gesendet wird, auf dem eine Funktion ausgeführt wird anfordern. Dies sorgt für eine einigermaßen effiziente Implementierung, da man so etwas unter Verwendung einer switch-case (Jump-Table-Optimierungen beiseite) nicht mehr tun muss. Zum Beispiel:Verwenden eines Arrays von Funktionszeigern auf Elementfunktionen in C++

Statt dessen:

switch(msg.cmd){ 
    case FUNC0: 
     return func0(msg); 

    case FUNC1: 
     return func1(msg); 

    ... 
} 

Wir so etwas tun könnte den entsprechenden Handler direkt auszuführen (Weglassen jede Vernunft Überprüfung auf msg.cmd):

(*cmd_functions[msg.cmd])(msg) 

Vor kurzem habe ich begann mit C++ - Code zu arbeiten, der ähnliche "Kontroll" -Funktionalität implementiert, aber ich bin dabei, einen switch-case zu verwenden. Gibt es dafür eine kanonische Methode in C++? Vielleicht eine Instanzvariable der Funktionszeiger-Array-Instanz, die im Konstruktor initialisiert wurde?

Ich war besorgt, die Lösung könnte ein wenig komplizierter aufgrund der Laufzeit Verwendung einer Klasse V-Tabelle sein.

+0

Funktioniert die C-Methode nicht immer noch in C++? – JeremyP

+0

Ich bin kein Experte für moderne C++, aber, da es nun [lambdas] (http://en.cppreference.com/w/cpp/language/lambda), können Sie nicht ein Array oder Vektor oder was auch immer von erstellen Lambda? – JeremyP

+0

Nur neugierig, warum steckst du mit einem Schalterkoffer fest? Warum würde ein Sprungtisch nicht funktionieren? – mnistic

Antwort

1

Die Standardlösung wäre in der Tat eine v-Tabelle: deklarieren Sie eine Basisklasse/Schnittstelle mit einer virtuellen Methode für jede Nachricht.

Sie benötigen eine - Anweisung, um die jeweilige Funktion aufzurufen - aber das würde Ihre Initialisierung der Funktionstabelle grundsätzlich ersetzen.

Sie würden sauberere Handhabung erhalten, die switch-Anweisung könnte sogar Parameterübersetzung ausführen (so dass die Nachrichtenhandler "sinnvolle" Argumente erhalten).

Sie würden einige "Zusammensetzbarkeit" der Tabelle verlieren, d. H. "Zuweisung" derselben Handler-Funktion zu verschiedenen, nicht verwandten konkreten Objekten.


Eine andere, allgemeinere Option würde die Elemente der Funktionszeiger mit std::function<void(MyMsg const &)> ersetzen - dies erlauben würde, nicht nur global/statische Funktionen zuweisen, sondern auch jede andere Klasse Member-Funktionen, Lambda-Ausdrücke und dergleichen. Sie könnten leicht zu bestehenden Funktionen weiterleiten, deren Signaturen nicht übereinstimmen.

Der Nachteil hier sind höhere Kosten der Initialisierung der Tabelle, sicne Konstruktion der std :: Funktion wird wahrscheinlich eine Zuordnung zumindest im allgemeinen Fall beinhalten. Außerdem würde ich zumindest vorläufig höhere Kosten pro Aufruf erwarten, da Sie typische v-table-spezifische Optimierungen verpassen würden.


An dieser Stelle könnten Sie auch eine andere Architektur betrachten wollen: zumindest für den Fall, dass es mehrere „Hörer“ zu jeder Nachricht ist, möchten Sie vielleicht ein Ereignisabonnement oder Signal/Slot-Design betrachten .


Natürlich können Sie auch so bleiben, wie Sie in C getan haben. Es ist in Ordnung.

Verwandte Themen