Lambda Funktion mit zwei Callbacks



  • Hallo,

    ich habe eine Frage, kann man Lambda Funktionen mit zwei Callbacks haben?

    Ich habe folgendes gefunden, jedoch wüsste ich die Syntax dazu nicht:

    void lib::XX::method(std::function<XX(const i&, int j)> control_callback, std::function<YY(const i&, int j)> control_callback2)
    

    Lambda mit einem Callback kenn ich, wie hier z.B. :

    [capture list] (parameter list) -> trailing-return-type {body}
    

    Wie könnte die Syntax aussehen für zwei Callbacks?

    So sieht bis jetzt die Funktion bei mir aus:

    car.control([&time,&initial_pose](const CarState car_state, Duration period) -> CartesianPose { ...body...});
    

    Gibt mir als return die neue x,y Position,will jedoch noch eine bewegung parallel ausführen, daher die zweite Callback und ich versteh nicht genau wie ich das laut der Doku (erster Code Schnipsel) tun soll.



  • Sorry, ich verstehe die Frage und das Problem nicht. Was Du mit "Callback" meinst, ist wahrscheinlich der Funktionsaufruf-Operator. Ein Lambda-Objekt hat genau einen solchen Operator, der aber Template-isiert sein kann.

    Hat Deine erste Code-Zeile (lib::XX::method) etwas mit der letzten (car.control) zutun?



  • Sorry für die schlechte Beschreibung, Lambda-Funktionen sind komplett neu für mich.

    Ja, ich habe ein Objekt "Car" welche control aufruft (mhh wobei wenn es einen Namen hat "control" ist es ja keine Lambda Funktion mehr oder?)

    Mit dem letzten Aufruf rufe ich einen Loop auf, der z.B. 10 Sekunden lang läuft.

    Ich kann immer nur einen Loop gleichzeitig laufen lassen, möchte aber gern zwei Rückgabewerte und der erste Schnipsel sieht danach aus mit callback 1 und 2



  • Callback schrieb:

    Sorry für die schlechte Beschreibung

    Es ist immer noch unverständlich.

    Wenn du ein Funktion mit zwei Parametern hast, musst du auch zwei Werte übergeben, egal ob es sich um int oder std::function Parameter handelt. In deinem Fall musst du vermutlich zwei Lambdas übergeben. Genaueres mit einer verständlichen Frage.



  • OK,

    also ich habe folgende Funktion die auch so funktioniert:

    car.control([&time,&initial_pose](const CarState car_state, Duration period) -> CartesianPose 
    { 
    ...hier im Body berechne ich 10 Sekunden die neuen Koordinaten und gebe bei jedem Durchlauf ein Array zurück
    return = new_pose});
    

    so damit bewegt sich mein "car", jedoch brauch ich parallel zu der Bewegung noch eine Rotation, in der doku hab ich folgendes gelesen:

    Das entspricht dem was ich nutze:
    void control(std::function<Torques(const CarState&, Duration)> control_callback)

    nun stand darunter noch eine andere Möglichkeit:
    void control(std::function<Torques(const CarState&, Duration)> control_callback, std::function<JointVelocities(const CarState&, Duration)> control_callback_2)

    Ich verstehe den Aufruf nicht ganz . bzw weiß nicht was ich mir genau darunter vorstellen kann. Also ich kann mir den zweiten Aufruf nicht aus dem ersten (den ich nutze) herleiten. Wie die Syntax aussehen würde. Sind das zwei Funktionsaufrufe?

    Ich kanns nicht besser erklären... worst case kann man den Thread hier auch schließen/löschen...

    trotzdem vielen Dank für die Mühe



  • Callback schrieb:

    Ich verstehe den Aufruf nicht ganz . bzw weiß nicht was ich mir genau darunter vorstellen kann. Also ich kann mir den zweiten Aufruf nicht aus dem ersten (den ich nutze) herleiten. Wie die Syntax aussehen würde. Sind das zwei Funktionsaufrufe?

    Das ist eine Funktion die mit zwei Lambdas (oder Funktionen oder Funktoren) aufgerufen wird. Das erste berechnet Torques, das zweite JointVelocities.

    Warum dein Lamda CartesianPose statt Torques berechnet solltest du selber wissen.

    Besser lesbar wird das, wenn man Variablen für die Lamdas verwendet:

    auto calculate_Torques = [&time,&initial_pose](const CarState car_state, Duration period) -> Torques {
    ...
    return ...
    };
    
    auto calculate_JointVelocities = [&time,&initial_pose](const CarState car_state, Duration period) -> JointVelocities{
    ...
    return ...
    };
    
    car.control( calculate_Torques, calculate_JointVelocities );
    


  • Callback schrieb:

    Lambda mit einem Callback kenn ich, wie hier z.B. :

    [capture list] (parameter list) -> trailing-return-type {body}
    
    [capture list] (parameter list) -> trailing-return-type {body}, [capture list 2] (parameter list 2) -> trailing-return-type-2 {body 2}
    

    tadaa



  • danke manni66, dein Kommentar hat schon geholfen.



  • Sorry ich nochmal,

    nur so als Idee, wenn ich die beiden Lambdas parallel als Threads laufen lassen will.

    auto calculate_Torques = [&time,&initial_pose](const CarState car_state, Duration period) -> Torques {
    ...
    return ...
    };
    
    auto calculate_JointVelocities = [&time,&initial_pose](const CarState car_state, Duration period) -> JointVelocities{
    ...
    return ...
    };
    
    std::thread t1(car.control( calculate_Torques));
    std::thread t2(car.control( calculate_JointVelocities));
    

    da kommt leider immer nur invalid use of void expression



  • da

    Äußerst präzise ...

    Du lässt das Ergebnis von car.control im Thread laufen. Das Ergebnis ist void und läuft daher nicht so gut.

    Lerne erst programmieren, danach kannst du dich mit Threads beschäftigen.



  • Callback schrieb:

    std::thread t1(car.control( calculate_Torques));
    std::thread t2(car.control( calculate_JointVelocities));
    

    da kommt leider immer nur invalid use of void expression

    (1) Pass auf, dass Du keine Datenrennen bekommst, bei sowas. Der C++ Compiler verhindert das nicht. Du musst wissen, was Du tust. Was macht denn car-control? Will die Methode das car-Objekt verändern? Da es nichte zurück gibt, muss es ja irgendwas verändern, sonst wäre der Aufruf nutzlos. Das würde dann sehr wahrscheinlich ein Datenrennen erzeugen. Wenn Du nicht weißt, was das ist, würde ich davon die Finger lassen. Wenn Du multi-threaded Programmierung verkackst, dann verhält sich das Programm sehr komisch und es ist verhältnismäßig schwierig rauszufinden, was genau du falsch gemacht hast.

    (2) Das Starten eines neues Threads kostet etwas Overhead. Falls Deine car-control Funktion relativ kurzlebig ist, wird sich das nicht lohnen. Eine Alternative sind dann noch Thread-Pools, in denen untätige Threads geparkt werden, die dann schneller auf etwas reagieren können.

    (3) Du versuchst hier Thread-Objekte zu erzeugen, indem du das Ergebnis von car.control(calculate_Torques) und car.control( calculate_JointVelocities) als Parameter für den Konstruktor verwendest. Du rufst deine Funktionen schon quasi auf innerhalb deines Haupt-Threads. Und die control-Funktion gibt anscheinend "void" zurück, was Du aber nicht als Parameter verwenden kannst. Deswegen die Fehlermeldung "invalid use of void expression". Dem thread -Konstruktor gibt man Funktion und Parameter einzeln über:

    void do_something_that_takes_a_while(int param1, double param2);
    
    int main() {
        std::thread t(do_something_that_takes_a_while, 42, 3.14159265);
        //    nicht t(do_something_that_takes_a_while(42, 3.14159265));
        t.join();
    }
    

    In deinem Fall scheint aber die control -Methode überladen oder ein Template zu sein, da Du zwei verschiedene Funktionsobjekte übergeben willst, die jeweils etwas anderes zurückgeben:

    void do_something_that_takes_a_while(int param1, double param2);
    void do_something_that_takes_a_while(int param1, std::string param2);
    
    int main() {
        std::thread t(do_something_that_takes_a_while, 42, 3.14159265);
        t.join();
    }
    

    Das wird so aber nicht funktionieren, weil es mehrdeutig ist. Es ist nicht klar, welches der do_something_that_takes_a_while -Funktionen du als ersten Parameter für den thread -Konstruktor übergeben willst. Deswegen würde ich generell einen Lambda-Ausdruck empfehlen:

    int main() {
        int p1 = 42;
        std::string p2 = "pi";
        std::thread t([p1, p2=std::move(p2)]{do_something_that_takes_a_while(p1,std::move(p2));});
        t.join();
    }
    

    Und falls Du die "generalized lambda captures", die es seit C++14 gibt, noch nicht benutzen kannst, kannst Du so ein "Move capture" auch umgehen, indem man das per Lambda-Funktions-Parameter entgegen nimmt:

    int main() {
        int p1 = 42;
        std::string p2 = "pi";
        std::thread t( [p1](std::string p2){
            do_something_that_takes_a_while(p1,std::move(p2));
        }, std::move(p2) );
        t.join();
    }
    

Log in to reply