[C++] Registrieren von Methoden - std::function, lambdas,...

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Antworten
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

[C++] Registrieren von Methoden - std::function, lambdas,...

Beitrag von smurfer »

Hallo zusammen,

mir fiel es nicht ganz leicht, einen passenden Betreff zu finden, und ich hoffe somit, dass die folgende Erläuterung verständlich ist: Ich möchte bei einer Klasse (bzw. deren Instanz) Methoden anderer Klassen (Module) registrieren, d.h. bekannt geben, dass ein Modul eine Methode zur Verfügung stellt und Zugriff auf diese bieten.
Die erstgenannte Klasse ist die Schnittstelle zu Webclient, Lua-Scripting und weiteren internen Modulen, eine abstrakte Schicht über der darunterliegenden Engine. Ein Lua-Script soll nun genau wie ein Webclient Methoden nutzen können (lesend wie schreibend), also bespielsweise get_objects oder set_time. Diese werden von den Engine-Modulen zur Verfügung gestellt und bei der Schnittstelle registriert.

So sagt ein Datenmanager etwas wie

Code: Alles auswählen

register("get_objects",...)
ein Systemmanager sinngemäß

Code: Alles auswählen

register("set_time",...)
Wie löse ich so etwas am Besten? Momentan schwebt mir etwas vor wie eine unordered_map in der Schnittstelle, die z.B. einen String als Schlüssel registriert und dahinter eine Methode über ein Lambda mittels std::function ablegt. Allerdings sehe ich noch nicht, wie ich mit verschiedenen Parametern, Rückgabewerten etc. umgehen soll. Dafür wieder abstrakte Interfaces scheint mir zu umständlich, gibt es dafür eine einfache Lösung oder ein Best-Practice?

Viele Grüße, smurfer
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Noch ein Nachtrag: Mir würde es schon helfen zu wissen, wonach ich genau suchen muss. Bislang war ich mit function/method registration, variadic functions/templates, lambda, functor, variable number of parameters, callback ... in diversen Kombinationen im Kontext C++ nicht sonderlich weit gekommen.

Hier mal ein kurz zusammen gehackter Ausschnitt ohne Anspruch auf finales Design, der mit einer bestimmten Art von Funktion (kein Rückgabewert, keine Parameter) das macht, was ich möchte:

Code: Alles auswählen

#ifndef COM_INTERFACE_H
#define COM_INTERFACE_H

//--- Standard header --------------------------------------------------------//
#include <functional>
#include <unordered_map>

/// Map of functions, accessed by name
typedef std::unordered_map<std::string, std::function<void(void)>> RegisteredFunctionsType;

////////////////////////////////////////////////////////////////////////////////
///
/// \brief Class providing an interface to the engine
///
////////////////////////////////////////////////////////////////////////////////
class CComInterface
{
    
    public:

        //--- Methods --------------------------------------------------------//
        void call(const std::string& _strName)
        {
            METHOD_ENTRY("CComInterface::call")
            if (m_RegisteredFunctions.count(_strName) != 0)
                m_RegisteredFunctions[_strName]();
            else
            {
                WARNING_MSG("Com Interface", "Unknown function <" << _strName << ">. ")
            }
        }
        bool registerFunction(const std::string& _strName, const std::function<void(void)>& _Function)
        {
            METHOD_ENTRY("CComInterface::registerFunction")
            m_RegisteredFunctions[_strName] = _Function;
            return true;
        }
    private:
        
        RegisteredFunctionsType m_RegisteredFunctions; ///< Registered functions provided by modules
      
};
#endif // COM_INTERFACE_H

Code: Alles auswählen

void quit()
{
    METHOD_ENTRY("quit")
    g_bDone = true;
}

int main(int argc, char *argv[])
{
    [...]
    
    CComInterface ComInterface;
    ComInterface.registerFunction("quit", quit);

    [...]

    // Quit the program
    ComInterface.call("quit");
}
Diese Funktionalität hätte ich nun gerne mit beliebigen Parametern und Rückgabewerten, aber außer ein Interface, welches für jede Art von Aufruf abgeleitet werden muss, ähnliches gilt für Funktoren, oder reiner C-Code, fällt mir bislang keine Lösung ein. Auch Template-Lösungen wie std::tuple benötigen für jeden Parametersatz andere Templateparameter und scheiden daher auch aus, oder übersehe ich etwas?

Beste Grüße, smurfer
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Gene »

Hi

Leider kann ich dir keinen Suchbegriff nennen nachdem Du suchen könntest. Aber es dürfe lösbar sein. Der trick ist über geschickte Vererbung das zu machen.

Code: Alles auswählen

class BaseCallbackFunction {
public:
  virtual ~BaseFunction() {}
};

template <typename ...Args>
class CallbackFunction : public BaseCallbackFunction {
  std::function<void(Args...)> mFunction;
public:
 void call(Args ....args) {
    mFunctions(args...);
  }
};

typedef std::unordered_map<std::string, std::unique_ptr<BaseCallbackFunction>> RegisteredFunctionsType;

[…]

        template<typename ...Args>
        void CComInterface::call(const std::string& _strName, Args... args)
        {
            METHOD_ENTRY("CComInterface::call")
            if (m_RegisteredFunctions.count(_strName) != 0)
                auto ptr = dynamic_cast<CalbackFunction<Args...>*>(m_RegisteredFunctions[_strName].get());
                if (ptr != nullptr ){
                  ptr->call(args...);
                } else {
                WARNING_MSG("Com Interface", "Known function with different signature <" << _strName << ">. ")
                }
            else
            {
                WARNING_MSG("Com Interface", "Unknown function <" << _strName << ">. ")
            }
        }
Zuletzt geändert von Gene am 24.08.2016, 10:41, insgesamt 1-mal geändert.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Hallo Gene,

vielen Dank schon mal! Ich werde die Variante, sobald ich wieder am Rechner zuhause bin, ausprobieren.

Beste Grüße
Helmut
Establishment
Beiträge: 237
Registriert: 11.07.2002, 15:49
Wohnort: Bonn
Kontaktdaten:

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Helmut »

Wenn es um Lua geht kannst du dir auch luabind anschauen.
Insgesamt würde ich aber diese ganzen Indirektionen versuchen zu vermeiden. Sowas zu Debuggen macht keinen Spaß.
NytroX
Establishment
Beiträge: 358
Registriert: 03.10.2003, 12:47

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von NytroX »

Ich würde mir an deiner Stelle mal ChaiScript anschauen.

Die benutzen C++11/14 magic um das hinzubekommen; sieht dann so aus:

Code: Alles auswählen

#include <chaiscript/chaiscript.hpp>

std::string helloWorld(const std::string &t_name) {
  return "Hello " + t_name + "!";
}

int main() {
  chaiscript::ChaiScript chai;
  chai.add(chaiscript::fun(&helloWorld), "helloWorld");

  chai.eval(R"(
    puts(helloWorld("Bob"));
  )");
}
Link zur Homepage: http://chaiscript.com
Source für das binding: https://github.com/ChaiScript/ChaiScrip ... nction.hpp
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Hallo zusammen,

Helmut, danke für den Hinweis. Es ging mir allerdings nicht primär um Lua-Bindings, Lua ist letztlich nur ein Nutzer der Schnittstelle. Im Endeffekt möchte ich kein ausgewachsenes internes Scripting, sondern lediglich eine Art flexible Functors. Als Ergänzung, es gibt eine ganz nette Übersicht und Bewertung der diversen Lua-Binding-Bibliotheken: http://realmensch.org/blog/fun-lua-bindings.

NytroX, vielen Dank, sieht sehr interessant aus. Selbst wenn ich ChaiScript nicht unmittelbar einbinden sollte (siehe obigen Kommentar an Helmut, es sollte keine komplette Scripting-Engine sein), lassen sich bestimmt interessante Implementierungsdetails aus dem Code lesen.

Beste Grüße
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von dot »

Nur mal rein prinzipiell: Was ist denn genau das Problem mit std::function?
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Hi dot,

std::function an sich ist kein Problem, das nutze ich ja auch -- siehe Beispiel zweiter Post. Allerdings muss ich dort nach meinem Verständnis fest die Templateparameter angeben, was spätestens dann schwierig wird, wenn ich die Funktionen mit verschiedenen Parametern und Rückgabewerten in der unordered_map speichere. So zumindest meinem naiven Gedanken nach.
Das Beispiel im zweiten Post funktioniert soweit, allerdings eben nur für einen Funktions- oder per Lambda-Delegate einen Methodentyp.

War das die Frage? Ich bin für jeden Vorschlag offen, mit manchen der neuen (C++11-)Möglichkeiten bin ich noch nicht so dicke ;)

Edit: Zu dem Thema, Gene, wie sähe denn dann nach Deinem Beispiel die Deklaration der map als Member aus und fehlt nicht die Ableitung von der Basisklasse beim spezialisierten Callback? So ganz habe ich es noch nicht raus.
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Gene »

Hi smurf

(Dies ist nicht bezogen auf deinen Edit, den hab ich erst jetzt gesehen)

Dir fehlt Überladung von Funktionen? Das kann man damit auch implementieren. Das Problem ist natürlich hier nur die unorderd_map:

Code: Alles auswählen

 typedef std::unordered_map<std::string, std::unique_ptr<BaseCallbackFunction>> RegisteredFunctionsType; 
Hier hab ich unterschiedliche Lösungsvorschläge:
1. mit einem Hilfsvektor

Code: Alles auswählen

 typedef std::unordered_map<std::string, std::vector<std::unique_ptr<BaseCallbackFunction>>> RegisteredFunctionsType; 
In diesem fall müssten man durch alle einträge in std::vector mit dynamic_cast testen um den richtigen eintrag herrauszubekommen.

2. mit einem anderen key für die unordered_map:

Code: Alles auswählen

 typedef std::unordered_map<std::pair<std::string, size_t>, std::unique_ptr<BaseCallbackFunction>> RegisteredFunctionsType;
[…]
        template<typename ...Args>
        void CComInterface::call(const std::string& _strName, Args... args)
        {
            std::pair<std::string, size_t> key;
            key.first = _strName;
            key.second = typeid(CallbackFunction<Args....>).hash_code();
            if (m_RegisteredFunctions.count(key) != 0)
            […]
schöne Grüße Gene
Zuletzt geändert von Gene am 24.08.2016, 10:41, insgesamt 1-mal geändert.
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Gene »

Hi

Ja die Ableitung fehlt. Ich korrigiere das oben im Post.
Was meinst du mit "Map" alse "Member?
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von dot »

smurfer hat geschrieben:Hi dot,

std::function an sich ist kein Problem, das nutze ich ja auch -- siehe Beispiel zweiter Post. Allerdings muss ich dort nach meinem Verständnis fest die Templateparameter angeben, was spätestens dann schwierig wird, wenn ich die Funktionen mit verschiedenen Parametern und Rückgabewerten in der unordered_map speichere. So zumindest meinem naiven Gedanken nach.
Nun, wenn du die Signatur der Funktion nicht kennst, kannst du sie aber auch nie aufrufen. Von da her ist mir nicht ganz klar, was genau der Sinn davon sein sollte, eine Funktion unbekannter Signatur zu "speichern"... ;)

PS: Ich frag nur blöd nach weil das, was du hier offenbar zu basteln suchst, mir nach einer unglaublich komplizierten Lösung für ein Problem aussieht, das man sich besser gar nicht erst machen würde... ;)
Zuletzt geändert von dot am 24.08.2016, 10:50, insgesamt 1-mal geändert.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Gene hat geschrieben:Hi

Ja die Ableitung fehlt. Ich korrigiere das oben im Post.
Was meinst du mit "Map" alse "Member?
Danke und das mit dem Member hat sich erledigt.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

dot hat geschrieben: Nun, wenn du die Signatur der Funktion nicht kennst, kannst du sie aber auch nie aufrufen. Von da her ist mir nicht ganz klar, was genau der Sinn davon sein sollte, eine Funktion unbekannter Signatur zu "speichern"... ;)
Dem will und kann ich nicht widersprechen :D. Ich war bislang davon ausgegangen, die Signatur (wieder eine Vokabel gelernt :)) bei der Deklaration der unordered_map kennen zu müssen. Falls ich Genes Vorschlag soweit richtig interpretiere, ist das nicht somit nicht mehr der Fall, dann muss ich die Signatur nur noch an der Stelle oder in dem Modul kennen, welches die Methode/Funktion anbieten und registrieren möchte -- das ist ja völlig in Ordnung.
Ich muss noch einmal die Ruhe und Zeit finden, einzubinden und in meinem Kontext zu testen, wahrscheinlich ist es dann damit gelöst. Vielen Dank Euch soweit.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

dot hat geschrieben:PS: Ich frag nur blöd nach weil das, was du hier offenbar zu basteln suchst, mir nach einer unglaublich komplizierten Lösung für ein Problem aussieht, das man sich besser gar nicht erst machen würde... ;)
Mag auch sein, blöde Gegenfrage: Wie kann ich es unkompliziert machen :)?

Mir geht es letztlich darum, dass ich von außen (Lua und andere Clients) Zugriff auf Teile der Engine benötige, die in verschiedenen Modulen liegen (Physik, System,...) und auch voraussichtlich recht häufig erweitert werden. Da sah ich es als komfortable Lösung, bereitgestellten Zugriff in der dargestellen Form möglichst schnell zu registrieren.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von dot »

smurfer hat geschrieben:
dot hat geschrieben: Nun, wenn du die Signatur der Funktion nicht kennst, kannst du sie aber auch nie aufrufen. Von da her ist mir nicht ganz klar, was genau der Sinn davon sein sollte, eine Funktion unbekannter Signatur zu "speichern"... ;)
Dem will und kann ich nicht widersprechen :D. Ich war bislang davon ausgegangen, die Signatur (wieder eine Vokabel gelernt :)) bei der Deklaration der unordered_map kennen zu müssen.
Musst du auch. Die Frage ist: Wieso musst du einen Haufen Funktionen, die nichts miteinander zu tun haben, in der selben Map speichern?
smurfer hat geschrieben:Mir geht es letztlich darum, dass ich von außen (Lua und andere Clients) Zugriff auf Teile der Engine benötige, die in verschiedenen Modulen liegen (Physik, System,...) und auch voraussichtlich recht häufig erweitert werden. Da sah ich es als komfortable Lösung, bereitgestellten Zugriff in der dargestellen Form möglichst schnell zu registrieren.
Deine Engine muss doch irgendein Interface haben!? Wenn du ein Lua Binding willst, musst du eben ein Lua Binding bauen. Was genau eine Map von Funktionspointern da helfen soll, ist mir ein bisschen ein Rätsel... ;)
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Gene »

dot hat geschrieben:
smurfer hat geschrieben:
dot hat geschrieben: Nun, wenn du die Signatur der Funktion nicht kennst, kannst du sie aber auch nie aufrufen. Von da her ist mir nicht ganz klar, was genau der Sinn davon sein sollte, eine Funktion unbekannter Signatur zu "speichern"... ;)
Dem will und kann ich nicht widersprechen :D. Ich war bislang davon ausgegangen, die Signatur (wieder eine Vokabel gelernt :)) bei der Deklaration der unordered_map kennen zu müssen.
Musst du auch. Die Frage ist: Wieso musst du einen Haufen Funktionen, die nichts miteinander zu tun haben, in der selben Map speichern?
Also natürlich musst du die Signatur der unordered_map kennen. Aber es ist nicht notwendig die Signatur der Funktionen zu kennen, die in der unordered_map gespeichert werden.
Diese müssen bekannt sein wenn die Funktion zur map hinzugefügt wird und wenn die Funktion aus der map aufgerufen wird. Dies ist aber in beiden Fällen trivialerweise gegeben.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

dot hat geschrieben:Musst du auch. Die Frage ist: Wieso musst du einen Haufen Funktionen, die nichts miteinander zu tun haben, in der selben Map speichern?
Jetzt geraten wir wieder in die gleiche Richtung wie neulich bei der -- durchaus interessanten -- Diskussion zu Downcasts ;) . Falls "nichts miteinander zu tun haben" für Dich über die Signatur definiert ist, stimmt es natürlich. Allerdings gehören die Funktionen rein logisch für mich sehr wohl zusammen. So ist z.B. ein setRadius für mich auf gleicher logischer Ebene wie ein setPosition, dennoch unterscheiden sich die Parameter und damit die Signatur. Die Umsetzung sind sprachliche Mittel zum Zweck. Dafür die passenden zu finden, ist genau der Anlass meiner Anfrage. Im Prinzip gefällt mir Genes Lösung schon recht gut.
dot hat geschrieben:Deine Engine muss doch irgendein Interface haben!? Wenn du ein Lua Binding willst, musst du eben ein Lua Binding bauen. Was genau eine Map von Funktionspointern da helfen soll, ist mir ein bisschen ein Rätsel... ;)
Ich möchte im Idealfall, dass kein Nutzer der Methoden etwas über die Module wissen muss, von denen diese kommen, sondern nur, dass es ein zentrales Kommuniktationsinterface gibt. Zu Deiner Anmerkung, das Lua-Modul würde sich entsprechend direkt and das Interface, nicht an dessen Module hängen.
Nennen wir sie mal Backend- und Frontend-Module:

Server

Backend:.......Simulation, Physik, System, ...
..............................\.............|......../
................................ComInterface
............................/............|...............\
Frontend:..........Lua,.. Netzwerk,... lokale Grafik
................................./.......|........\
Client..................Web, Terminal, GUI

Z.B. sollte das Lua-Modul Zugriff auf Objekte (des Physikmoduls) haben, gleichzeitig aber auch auf Aspekte des Simulationsmoduls, wie Simulationsfrequenz etc., sowie auf das System (Programmende, Pause). Gleiches gilt für das Netwerkmodul, welches all diese Informationen zu einem Terminal-Client oder einem grafischen Client schickt.

Ich hoffe es ist etwas klarer geworden.

Edit: Kurzer Nachtrag zum Verständnis: Das ComInterface könnte genauso eine Klasse mit lauter Methoden sein, die die Anfragen einfach nur weiterleiten (das wären dann wohl delegates?!). Dann müsste das Interface alle Backend-Module kennen. Vom Gefühl her fand ich es sinnvoller, dass alle Module das Interface kennen und jeder melden kann, was er bereitstellt. Ein Grund für das ComInterface war zudem, dass es an einer zentralen Stellen einen Datenspeicher gibt, der für potentielle Frontends einen konsistenten Zustand bereit hält, unabhängig von im Hintergrund laufenden Berechnungen. Auf genau den greift das ComInterface zu, so dass die Frontends und letztlich Clients genau mit diesem konsistenten Zustand arbeiten können.

Edit 2: Zurzeit bin ich allerdings ein wenig zwiegespalten, da bei Genes Lösung für jede Methode ein Helferlein geschrieben werden müsste, dafür an der gefühlt richtigen Stelle. Ich suche einfach etwas, womit ich mit einem Kommando dem Interface mitteilen kann: "Du brauchst XY? Habe ich schon, stelle ich Dir bereit" und das sinngemäß mittels register(etwas_schon_vorhandenes).
Gene
Beiträge: 25
Registriert: 22.05.2003, 11:26

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von Gene »

smurfer hat geschrieben:[...]
Edit 2: Zurzeit bin ich allerdings ein wenig zwiegespalten, da bei Genes Lösung für jede Methode ein Helferlein geschrieben werden müsste, dafür an der gefühlt richtigen Stelle. Ich suche einfach etwas, womit ich mit einem Kommando dem Interface mitteilen kann: "Du brauchst XY? Habe ich schon, stelle ich Dir bereit" und das sinngemäß mittels register(etwas_schon_vorhandenes).
An welcher Stelle benötigst du Helferlein? Man benötigt man einen kleinen Lambdaausdruck um Methoden von Klassen zu regestrieren. Für freie/globale Funktionen ist das nicht notwendig.
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Hallo zusammen,

Gene, Du hast Recht, die Helferlein halten sich in Grenzen. Ich habe jetzt einmal Zeit gefunden, deinen Ansatz bei mir zu implementieren und es funktioniert wie gewünscht, danke :) . Der Registrierungsaufruf bleibt dank Lambdas und entsprechendem Constructor recht übersichtlich, hier ein Bsp. direkt aus dem Code:

Code: Alles auswählen

ComInterface.registerFunction("cycle_camera",new CallbackFunction<>([&](){pVisualsManager->cycleCamera();}));
Ich werde bestimmt noch auf die ein oder andere Hürde stoßen, bis dahin passt es soweit, danke an alle für die guten Ideen und interessanten Anmerkungen.

Grüße, smurfer
odenter
Establishment
Beiträge: 207
Registriert: 26.02.2009, 11:58

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von odenter »

Wenn ich Dich richtig verstanden habe willst Du eine Liste von Kommandos erzeugen und dritten Ermöglichen diese Kommandos zu starten bzw. auszuführen.

Guck Dir doch mal die Quellen hiervon an:
http://www.oocities.org/standard_template/irrconsole/

Dies ist eine Ingameconsole, also Befehl eintippen und dann wird etwas ausgeführt.
Als Benutzer dieser Befehle muss ich nicht wissen wie die intern funktionieren. Die machen halt was, ich kann mir aber z.B. die Beschreibung anzeigen lassen welche der Programmierer eingepflegt hat.
Es muss allerdings für jedes Kommando eine Klasse erzeugt werden. Im Vergleich zu Deinem Ansatz klingt das erstmal nach mehr Arbeit, ich persönlich finde es allerdings besser. Vor allem was Fehlersuche und Behebung angeht. Außerdem kann man so den Zugriff auf die "internen" Methoden nochmal kapseln/wrappen what ever man machen will.

Die Registrieung eines Kommandos bei mir sieht wie folgt aus:

Code: Alles auswählen

	void irrConsole::LoadDefaultCommands(irr::IrrlichtDevice* device)
{
	ICommand* cmd = 0;

	cmd = new irrConsoleCommandEcho;
	RegisterCommand(cmd);
	
	cmd = new irrConsoleCommandHelp;
	RegisterCommand(cmd);

	cmd = new irrConsoleCommandList;
	RegisterCommand(cmd);
	
	cmd = new irrConsoleCommandDriverInfo(Helper::IrrlichtHelper::GetInstance()->Device());
	RegisterCommand(cmd);

	cmd = new irrConsoleCommandExit;
	RegisterCommand(cmd);

	cmd = new irrConsoleCommandQuit;
	RegisterCommand(cmd);

  cmd = new irrConsoleCommandSetApplicationVariable;
  RegisterCommand(cmd);
}
ausgeführt wird es wie folgt:

Code: Alles auswählen

void IDispatcher::dispatch(const String cmdName, const irr::core::array<String>& args, IMessageSink* pOutput)
{
  std::map<String, ICommand*>::iterator iter = commandTable.find(cmdName);
	if(iter != commandTable.end())
	{
		try
		{
			iter->second->invoke(args, this, pOutput);
			pOutput->AppendMessage(L"");
		}
		catch(irrConsoleError& err)
		{
			String wstr = L"error of type ";
			wstr += err.GetType();
			wstr += L" in invoking command [";
			wstr += cmdName;
			wstr += L"]";
			pOutput->logError(wstr);
			pOutput->AppendMessage(err.GetMessage());
		}
		catch(std::exception& ex)
		{
			String wstr = L"error in invoking command [";
			wstr += cmdName;
			wstr += L"]";
			pOutput->logError(wstr);
			pOutput->AppendMessage(gf_ToString(ex.what()));
		}
	}
	else
	{
		String wstr = L"command [";
		wstr += cmdName;
		wstr +=L"] is not a valid command";
		pOutput->logError(wstr);
		pOutput->AppendMessage(L"for a list of messages type \"help\" or \"list\"");
	}
}
Befehl echo

Code: Alles auswählen

class irrConsoleCommandEcho : public ICommand
{
public:
	irrConsoleCommandEcho();
	virtual ~irrConsoleCommandEcho();
	bool invoke(const irr::core::array<String>& args, IDispatcher* pDispatcher, IMessageSink* pOutput);
};


irrConsoleCommandEcho::irrConsoleCommandEcho() : ICommand(L"echo")
{
	SetUsage(L"echo <string>");
	AddDescLine(L"This command echoes the given string to console");
}

irrConsoleCommandEcho::~irrConsoleCommandEcho()
{
}

bool irrConsoleCommandEcho::invoke(const irr::core::array<String>& args, IDispatcher* pDispatcher, IMessageSink* pOutput)
{
	if(args.size() >= 1)
	{
		String wstr = args[0];
		for(irr::u32 i = 1; i < args.size(); i++)
		{
			wstr += L" ";
			wstr += args[i];
		}
		pOutput->AppendMessage(wstr);
	}
	return true;
}
Befehl driver_info

Code: Alles auswählen

class irrConsoleCommandDriverInfo : public ICommand
{
public:
	irrConsoleCommandDriverInfo(irr::IrrlichtDevice *pDevice);
	virtual ~irrConsoleCommandDriverInfo();
	bool invoke(const irr::core::array<String>& args, IDispatcher* pDispatcher, IMessageSink* pOutput);
private:
	irr::IrrlichtDevice *device;
};
irrConsoleCommandDriverInfo::irrConsoleCommandDriverInfo(irr::IrrlichtDevice *pDevice) : ICommand(L"driver_info"), device(pDevice)
{
	SetUsage(L"driver_info");
	AddDescLine(L"This command prints some info about the engine");

}
irrConsoleCommandDriverInfo::~irrConsoleCommandDriverInfo()
{
	device = 0;
}
bool irrConsoleCommandDriverInfo::invoke(const irr::core::array<String>& args, IDispatcher* pDispatcher, IMessageSink* pOutput)
{
	if(device)
	{
		irr::video::IVideoDriver* driver = device->getVideoDriver();
		String wstr = L" Irrlicht Version : ";
		wstr += gf_ToString(device->getVersion());
		pOutput->AppendMessage(wstr);

		// TODO: prüfen
		//wstr = L" OS Version : ";
		//wstr += device->getOSOperator()->getOperatingSystemVersion();
		//pOutput->AppendMessage(wstr);

		wstr = L" Display Driver : ";
		wstr+= device->getVideoDriver()->getName();
		pOutput->AppendMessage(wstr);

		wstr=L"";
		return true;
	}
	else
	{
		throw irrConsoleError(L"No valid irrlicht device detected!!");
	}
}
Aufruf z.B. so:

Code: Alles auswählen

\echo 12345
\echo Fuchs
\driver_info

\help driver_info // gibt hilfe zu Befehl driver_info aus
\help echo          // gibt Hilfe zu Befehl echo aus
smurfer
Establishment
Beiträge: 195
Registriert: 25.02.2002, 14:55

Re: [C++] Registrieren von Methoden - std::function, lambdas

Beitrag von smurfer »

Hi odenter, danke für den ausführlichen Hinweis, werde ich mir auch bei Gelegenheit näher anschauen. Viele Grüße
Antworten