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

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

Beitragvon smurfer » 21.08.2016, 21:22

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: Ansicht erweitern :: Alles auswählen
register("get_objects",...)

ein Systemmanager sinngemäß
Code: Ansicht erweitern :: 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
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon smurfer » 22.08.2016, 20:05

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: Ansicht erweitern :: 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: Ansicht erweitern :: 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
smurfer
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon Gene » 23.08.2016, 01:40

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: Ansicht erweitern :: 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.
Gene
 
Beiträge: 25
Registriert: 22.05.2003, 11:26

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

Beitragvon smurfer » 23.08.2016, 09:25

Hallo Gene,

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

Beste Grüße
smurfer
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon Helmut » 23.08.2016, 14:26

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ß.
Helmut
Establishment
 
Beiträge: 227
Registriert: 11.07.2002, 15:49
Wohnort: Bonn

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

Beitragvon NytroX » 23.08.2016, 23:02

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

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

Code: Ansicht erweitern :: Alles auswählen

#include <chaiscript/chaiscript.hpp>

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

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

  chai.eval(R"(
    puts(helloWorld("
Bob"));
  )"
);
}
 


Link zur Homepage: http://chaiscript.com
Source für das binding: https://github.com/ChaiScript/ChaiScript/blob/develop/include/chaiscript/dispatchkit/register_function.hpp
NytroX
Establishment
 
Beiträge: 135
Registriert: 03.10.2003, 12:47

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

Beitragvon smurfer » 24.08.2016, 09:17

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
smurfer
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon dot » 24.08.2016, 09:57

Nur mal rein prinzipiell: Was ist denn genau das Problem mit std::function?
Benutzeravatar
dot
Michael Kenzel
Establishment
 
Beiträge: 1601
Registriert: 06.03.2004, 19:10

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

Beitragvon smurfer » 24.08.2016, 10:07

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.
smurfer
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon Gene » 24.08.2016, 10:38

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: Ansicht erweitern :: 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: Ansicht erweitern :: 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: Ansicht erweitern :: 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

Beitragvon Gene » 24.08.2016, 10:40

Hi

Ja die Ableitung fehlt. Ich korrigiere das oben im Post.
Was meinst du mit "Map" alse "Member?
Gene
 
Beiträge: 25
Registriert: 22.05.2003, 11:26

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

Beitragvon dot » 24.08.2016, 10:48

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.
Benutzeravatar
dot
Michael Kenzel
Establishment
 
Beiträge: 1601
Registriert: 06.03.2004, 19:10

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

Beitragvon smurfer » 24.08.2016, 10:49

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
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon smurfer » 24.08.2016, 10:55

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
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

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

Beitragvon smurfer » 24.08.2016, 10:59

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.
smurfer
 
Beiträge: 57
Registriert: 25.02.2002, 15:55

Nächste

Zurück zu Algorithmen und Datenstrukturen

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast