callback mit zusätzlichem parameter



  • Hi,

    meine C++ kenntnisse sind begrenzt, deshalb schonmal sorry falls die frage dumm gestellt ist.

    ich möchte eine funktion aus einer bibliothek benutzen welche unter anderem eine funktion als argument übernimmt, also:
    void ziel(void (*func)(double x));

    dafür muss ich also eine funtion "void f(double x)" schreiben, dann "ziel(f);" und es klappt.

    nun möchte ich aber einen zusätzlichen parameter drin haben, also f(x,a).
    anschaulich möchte ich also f2(x)=f(x,a=const) und dann "ziel(f2);"

    ich möchte also während der laufzeit jede menge funktionen f2(x) erzeugen indem ich f(x,a) für feste a aufrufe, und die dann an ziel übergeben.

    wie geht das?

    Danke schonmal !



  • Geht nicht. Die Signatur Deiner Bibliotheksfunktion ist ja vorgegeben. Wäre die zB:
    void bibFunk(int x);
    könntest Du sie auch nicht einfach mit
    bibFunk(x, y);
    aufrufen.



  • Hallo. 🙂

    Das geht so leider nicht schön. Man könnte Lambdas oder std::bind nutzen um ein Funktionsobjekt zu erzeugen, aber da die Funktionszeiger statt Templates nutzen, hilft dir das nicht. Du kannst natürlich weiterhin mit statischen Membern + Funktionen arbeiten.

    struct blubb
    {
      static int i = 5;
      static void func(double d)
      {
        // Mach was mit d und i
      }
    };
    

    Aber wie gesagt, das ist halt recht unschön. Aber wenn die Bibliothek kein vernünftiges Interface anbietet, kann man da nichts machen. Mit fällt gerade auch nicht ein, wie man das wrappen könnte.



  • Bitte mehr Informationen. Ist die Bibliothek wirklich auf Funktionszeiger beschränkt oder hast Du da ein Wörtchen mitzureden? Es gibt da nämlich noch andere Möglichkeiten, die weniger beschränkt sind.

    Wenn es wirklich reine Funktionszeiger sein müssen, dann klappt das nur mit integralen, zur Compile-Zeit bekannten Konstanten:

    template<int A>
    void f2(int x) {return f(x,A);}
    
    :::
    
    int main()
    {
      ziel(f2<42>);
    }
    


  • es geht ganz konkret um diese funktion:
    http://www.alglib.net/translator/man/manual.cpp.html#sub_autogkintegrate

    und wenn ich schon so konkret werde, dann gleich richtig:

    class FE{
    public:
      void func(double& y, double x, int n);
    
    //wie schreibe ich diese Funktion und wo?
      void func_helper(double x, double xminusa, double bminusx, double &y, void *ptr);
    
      void integrateFunc();
    };
    
    void FE::func(double& y, double x, int n){
      y = n*x;
    }
    
    void FE::func_helper(double x, double xminusa, double bminusx, double &y, void *ptr){
      func(y,x,n);
    }
    
    void FE::integrateFunc(){
      double a = -1.0;
      double b = 1.0;
      alglib::autogkstate s;
      double v;
      alglib::autogkreport rep;
    
      int n=3; //dieses n muss er beim aufruf von func_helper noch kennen ! nur wie?
      alglib::autogksmooth(a, b, s);
      alglib::autogkintegrate(s, func_helper); //das hier brauch ich. das drumrum steht im example des links.
      alglib::autogkresults(s, v, rep);
    }
    

    jetzt möchte ich es schaffen für viele n die funktion aufzurufen. idealerweise ohne irgendwelche funktionen zu schreiben die außerhalb der klasse sitzen.



  • Du hast hier anfangs etwas sehr interessantes verschwiegen ... nämlich, dass die Bibliothek nicht so

    void ziel(void (*func)(double x));
    

    sondern so

    void ziel(void (*func)(double x, void*),void*);
    

    gestrickt ist. Das heißt, Du hast die Möglichkeit eigene Daten über diesen void*-Zeiger an die Funktion zu geben. Das ist typisch für solche Callback-Schnittstellen in C. In modernem C++ hätte man das wahrscheinlich eleganter über Templates oder so etwas wie std::function<> erledigt.

    Außerdem scheint Dir nicht klar zu sein, dass Elementfunktionen mit solchen Funktionszeigern inkompatibel sind. Um Elementfunktionen aufrufen zu können, brauchst Du natürlich auch ein passendes Objekt dazu. Woher soll die Bibliothek ein passendes Objekt hernehmen?

    Hier ist mal ein Tipp, der dir auf die Sprünge helfen sollte:

    #include <iostream>
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i,user_data) << '\n';
      }
    }
    
    int multipliziere(int a, int b)
    {
      return a*b;
    }
    
    struct meine_daten
    {
      int faktor;
    };
    
    int meine_funktion(int x, void* daten)
    {
      meine_daten & md = *static_cast<meine_daten*>(daten);
      return multipliziere(x,md.faktor);
    }
    
    int main()
    {
      meine_daten md = {11};
      altmodische_schnittstelle(meine_funktion,&md);
    }
    

    In "modern" kann das Ganze aber so aussehen:

    #include <iostream>
    #include <functional>
    
    void neumodische_schnittstelle(std::function<int(int)> func)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i) << '\n';
      }
    }
    
    int multipliziere(int a, int b)
    {
      return a*b;
    }
    
    int main()
    {
      using namespace std::placeholders;
      int n = 11;
      altmodische_schnittstelle(std::bind(multipliziere,_1,n));
    }
    

    wahlweise auch mit einer Lambdafunktion.



  • krümelkacker schrieb:

    In "modern" kann das Ganze aber so aussehen:

    Oder mit einem Template, was wesentlich effizienter sein dürfte, wenn die Funktion sehr oft aufgerufen wird.



  • cooky451 schrieb:

    Oder mit einem Template, was wesentlich effizienter sein dürfte, wenn die Funktion sehr oft aufgerufen wird.

    Das erwähnte ich bereits weiter oben.



  • Äh.. stimmt. Aber ein kleines Beispiel hätte doch nicht weh getan: 🙂

    template <typename F>
    void neumodische_schnittstelle(F func)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i) << '\n';
      }
    }
    


  • krümelkacker schrieb:

    Hier ist mal ein Tipp, der dir auf die Sprünge helfen sollte:

    #include <iostream>
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i,user_data) << '\n';
      }
    }
    
    int multipliziere(int a, int b)
    {
      return a*b;
    }
    
    struct meine_daten
    {
      int faktor;
    };
    
    int meine_funktion(int x, void* daten)
    {
      meine_daten & md = *static_cast<meine_daten*>(daten);
      return multipliziere(x,md.faktor);
    }
    
    int main()
    {
      meine_daten md = {11};
      altmodische_schnittstelle(meine_funktion,&md);
    }
    

    danke das ist schonmal sehr gut.
    es gibt allerdings noch das problem dass das ganze innerhalb einer klasse passiert. also

    #include <iostream>
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i,user_data) << '\n';
      }
    }
    
    class FE{
     public:
      int multipliziere(int a, int b)
      {
        return a*b;
      }
    
      struct meine_daten
      {
        int faktor;
      };
    
      int meine_funktion(int x, void* daten)
      {
        meine_daten & md = *static_cast<meine_daten*>(daten);
        return multipliziere(x,md.faktor);
      }
    
      void zweite_funktion()
      {
        meine_daten md = {11};
    	altmodische_schnittstelle(&FE::meine_funktion,&md);
      }
    };
    int main()
    {
      FE blue;
      blue.zweite_funktion();
    }
    

    der fehler ist "error C2664: 'altmodische_schnittstelle': Konvertierung des Parameters 1 von 'int (__thiscall FE::* )(int,void *)' in 'int (__cdecl *)(int,void *)' nicht möglich"

    google sagte mir dass ich das problem nicht innerhalb der klasse gelöst kriege, also eine funktion ausserhalb der klasse nutzen muss und da dann reinterpret_cast.

    ich würde aber sehr ungern die klasse nach außen öffnen.

    was kann ich tun damit es trotzdem klappt?



  • Nimm eine statische Funktion und pack den this pointer mit in die Userdaten.



  • #include <iostream>
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i,user_data) << '\n';
      }
    }
    
    class FE{
     public:
      int multipliziere(int a, int b)
      {
        return a*b;
      }
    
      struct meine_daten
      {
        int faktor;
      };
    
      int meine_funktion(int x, void* daten)
      {
        meine_daten & md = *static_cast<meine_daten*>(daten);
        return multipliziere(x,md.faktor);
      }
    
      void zweite_funktion()
      {
        meine_daten md = {11};
    	altmodische_schnittstelle(&FE::meine_funktion,&md);
      }
    };
    
    int main()
    {
      FE blue;
      blue.zweite_funktion();
    }
    

    Das Beispiel ist "innerhalb der Klasse" schon recht sinnfrei. Du hast da nirgends irgendwelche Datenelemente, die genutzt werden können. Wie gesagt, Dir ist auch der Unterschied zwischen einer nicht-statischen Elementfunktion und einer freien Funktion nicht klar.

    Das hier ist wohl eher Deine Situation inklusive Lösung:

    #include <iostream>
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i,user_data) << '\n';
      }
    }
    
    class DeineKlasse
    {
      int dies;
      int unddas;
    public:
      :::
      int nicht_statische_funktion(int x, int y) const;
    };
    
    int DeineKlasse::nicht_statische_funktion(int x, int y) const;
    {
      return dies*x + unddas*y;
    }
    
    struct kontext
    {
      DeineKlasse* objekt_zeiger;
      int y;
    };
    
    int freie_funktion(int x, void* userdata)
    {
      kontext& k = *static_cast<kontext*>(userdata);
      return k.objekt_zeiger->nicht_statische_funktion(x,k.y);
    }
    
    int main()
    {
      DeineKlasse deinObjekt :::
      :::
      kontext k = {&deinObjekt,11};
      altmodische_schnittstelle(freie_funktion,&k);
    }
    

    Und weil's in "modern" so schön ist, hier nochmal eine zumindest oberflächlich Funktionstemplate-freie aber typsichere und ohne void* auskommende Version:

    #include <iostream>
    #include <functional>
    
    void neumodische_schnittstelle(std::function<int(int)> func)
    {
      for (int i=0; i<10; ++i) {
        std::cout << func(i) << '\n';
      }
    }
    
    class DeineKlasse
    {
      int dies;
      int unddas;
    public:
      :::
      int nicht_statische_funktion(int x, int y) const;
    };
    
    int DeineKlasse::nicht_statische_funktion(int x, int y) const;
    {
      return dies*x + unddas*y;
    }
    
    int main()
    {
      using namespace std::placeholders;
      DeineKlasse deinObjekt :::
      :::
      neumodische_schnittstelle(
        std::bind(&DeineKlasse::nicht_statische_funktion, // welche Funktion?
                  &deinObjekt, // auf welchem Objekt soll die Funktion aufgerufen werden?
                  _1, // Platzhalter, Wert für x kommt aus der Bibliothek
                  11) // die Konstante für y, die Du vorgeben willst
      );
    }
    

    Alternativer Aufruf per Lambda-Ausdruck (IMHO zu bevorzugen):

    neumodische_schnittstelle([&](int x){
        return deinObjekt.nicht_statische_funktion(x,11);  
      });
    }
    


  • eine loesung mit struct und einer static function wollte ich gerade hier posten, da sehe ich ihr habt das auch schon vorgeschlagen 🙂

    vielen dank, ihr habt mir sehr geholfen !



  • PicassoYo, noch was als Beispiel :

    #include <iostream> 
    
    void altmodische_schnittstelle(int (*func)(int,void*), void* user_data) 
    { 
      for (int i=0; i<10; ++i) { 
        std::cout << func(i,user_data) << '\n'; 
      } 
    } 
    struct multiplikator
    {
       int faktor;
    
       static int callback (int x, void* _this )
       {
          return x * static_cast< multiplikator*>(_this)->faktor;
       }
    };
    
    int main() 
    { 
      multiplikator  m = { 11 };
    
      altmodische_schnittstelle (multiplikator::callback, &m);
    
    }
    

Anmelden zum Antworten