[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
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