Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

14.05.2017, 21:16

[C++] std::map mit lambads und optionalen parameter

Hallo Leute,

um mir C++ anzueignen arbeite ich an einem Projekt welcher im Grunde nur ein SDL Wrapper ist. Die "Architektur" der Frameworks wird in etwa so aussehen:

Man hat mehrere Szenen(wobei man nur eine Darstellen kann) in der Szene definiere ich die Objekte und hinterlege diese im Entity Manager. Dieser wird später verschiedene Events der Objekte abfeuern und die Objekte reagieren drauf.

Es wird mehrere Grundlegende Objekte geben und diese kann man dann erweitern, entweder durch Vererbung oder lambdas.

Nach einigen Stunden Entwicklung kann ich nun ein Rechteck mit definierter Größe/Farbe animieren.

Die Animation wird durch so ein lambda ausgelöst https://github.com/BlackScorp/DwarfForge…ene.cpp#L62-L69

Nun möchte ich noch weitere Events erstellen und auch mit Parametern. In PHP kann ich einem Lambda so viele Parameter übergeben wie ich möchte. In C++ ist es natürlich etwas komplexer und Stackoverflow zeigt mir Antworten mit denen ich nichts anfangen kann.

Was ich im Grunde möchte: Ich habe hier eine Methode "trigger" https://github.com/BlackScorp/DwarfForge…ce/Entity.h#L48 mit einer optionalen Struktur, wenn ich nun hier https://github.com/BlackScorp/DwarfForge…/Entity.cpp#L50 den Parameter übergeben möchte, bekomme ich beim Kompilieren ein Fehler:

" no match for call to '(std::map<std::__cxx11::basic_string<char>, std::function<void()> >::mapped_type {aka std::function<void()>}) (EventData*&)'"

Wenn ich C++ richtig interpretiere, will der Compiler mir sagen, dass hier https://github.com/BlackScorp/DwarfForge…ce/Entity.h#L52 nicht definert wurde, dass das Lambda auch ein Parameter EventData haben kann.

also habe ich das geschrieben:

C-/C++-Quelltext

1
2
private:
    std::map<std::string,std::function<void(EventData *data)>> bindings;


Damit entstehen noch mehr Compiler Fehler. Letztendlich musste ich noch EventData parameter dranhängen und bekam folgendes:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
class Entity {
public:
    std::string getId();
    void setId(std::string id);
    virtual void onDraw(SDL_Renderer* renderer, float interpolation);
    virtual void on(std::string eventName,std::function<void (EventData *data)> callback);
    virtual void trigger(std::string eventName,EventData *data = nullptr);
protected:
    std::string id;
private:
    std::map<std::string,std::function<void(EventData *data)>> bindings;
};


das Blöde ist, dass diese Definition mich dazu zwingt das Lambda mit einem Parameter zu definieren.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
  testRect->on("update", [testRect](EventData *data) {
        int x = testRect->getX();
        x++;
        if(x > 500){
            x = 0;
        }
        testRect->setX(x);
    });


ich wollte es aber optional machen. Wie muss die Definition für std::map aussehen? Ich komme nicht mehr weiter.

LG BlackScorp

Nimelrian

Alter Hase

Beiträge: 1 216

Beruf: Softwareentwickler (aktuell Web/Node); Freiberuflicher Google Proxy

  • Private Nachricht senden

2

14.05.2017, 22:36

Du könntest eine zusätzliche Überladung für on(std::string, std::function<void()>) erstellen, die die übergebene Funktion nochmal in einem Lambda wrappt, welches dann einen EventData Parameter erwartet, ihn aber nicht verwendet. Dein Compiler wird dann allerdings meckern, dass du da eine nicht verwendete Variable hast.
Ich bin kein UserSideGoogleProxy. Und nein, dieses Forum ist kein UserSideGoogleProxyAbstractFactorySingleton.

3

15.05.2017, 07:22

achso, stimmt ja C++ kann ja Methoden überladen, danke für den Tipp, ich werde es mal heute Abend ausprobieren. Dann ist es generell sinnfrei ein nulltpr als Parameter zu definieren oder?

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

4

15.05.2017, 07:23

Lambdas sind auch nur Funktionen. Die müssen also eine festgelegte Signatur haben, also eine feste Anzahl Parameter mit festgelegten Typen. Überlege Dir also, wie die Signatur Deiner trigger()-Funktion aussehen soll. Und gerade bei Lambdas hast Du dann doch leichtes Spiel, weitere Parameter per Capture mitzugeben. Dein trigger() sollte also nur die Parameter haben, die wirklich fundamental für den Betrieb notwendig sind.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

5

15.05.2017, 07:43


Und gerade bei Lambdas hast Du dann doch leichtes Spiel, weitere Parameter per Capture mitzugeben.


Mit Capture meinst du hier die eckigen Klammern? https://github.com/BlackScorp/DwarfForge…inScene.cpp#L76


Dein trigger() sollte also nur die Parameter haben, die wirklich fundamental für den Betrieb notwendig sind.

Die Grundlegende trigger Methode sollte ohne parameter auskommen, in einigen Fällen braucht man aber eins. Bzw in PHP/JavaScript würde ich sogar je nach dem unterschiedliche Anzahl an parametern Übergeben.

Ich glaube ich werde bei der Überladung ein Problem mit std::map haben, weil ich bestimmt für jede Überladung auch eine eigene std::map definieren muss oder? Wenn ja dann lass ich es sein und werde immer ein Parameter übergeben und nulltpr weglassen.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

6

15.05.2017, 09:01

Du legst im Template-Parameter der std::function die Funktionssignatur fest. Du brauchst also zwei Maps mit jeweils einer anderen std::function, um zwei verschiedene Funktionssignaturen aufzubewahren. Du kannst auch ne Mini-Ableitungshierarchie aufmachen, aber ganz ehrlich: wenn Du an der Stelle zwei verschiedene Funktionen brauchst, dann hast Du ein Design-Problem. Denn dann bedeutet diese Funktion anscheinend je nach Kontext was verschiedenes, und Du versuchst gerade, zwei eigentlich getrennte Abläufe in ein gemeinsames Interface zu quetschen. Dem Compiler kriegst Du das sicher irgendwie beigebogen, aber auf Dauer wirst Du darüber stolpern.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

7

15.05.2017, 09:11

Du legst im Template-Parameter der std::function die Funktionssignatur fest. Du brauchst also zwei Maps mit jeweils einer anderen std::function, um zwei verschiedene Funktionssignaturen aufzubewahren. Du kannst auch ne Mini-Ableitungshierarchie aufmachen, aber ganz ehrlich: wenn Du an der Stelle zwei verschiedene Funktionen brauchst, dann hast Du ein Design-Problem. Denn dann bedeutet diese Funktion anscheinend je nach Kontext was verschiedenes, und Du versuchst gerade, zwei eigentlich getrennte Abläufe in ein gemeinsames Interface zu quetschen. Dem Compiler kriegst Du das sicher irgendwie beigebogen, aber auf Dauer wirst Du darüber stolpern.


Jop, hab ich mir gedacht dass ich dann zwei oder mehrere maps brauche. Werde mal nulltpr rausbauen.

Werbeanzeige