Implementieren einer Callback-Funktion



  • Hallo,
    ich versuche eine Art Callback-Funktion zwischen zwei Klassen zu erstellen. Dabei soll der zweiten Klasse eine Callbackfunktion übergeben werden.

    class FirstClass{
       public:
          int Func(char* d, int x)    {     return x;      }      
    };
    
    class SecondClass{
      public:
          void Func( int(CallBak)(char* dat, int v))
         {
             char s[10];
             CallBak(s,5);
         }      
    };
    
    
    int main(int, char**)
    {
        FirstClass Fc;
        SecondClass Sc;
       
        Sc.Func(Fc.Func); // error: invalid use of non-static member function ‘int FirstClass::Func(char*, int)’
        
        return 0;
    }
    

    Der Compiler gibt mir die Fehlermeldung "error: invalid use of non-static member function ‘int FirstClass::Func(char*, int)’" aus.
    Wenn ich das Schlüsselwort static bei "Func" in FirstClass hinzufüge, funktioniert es. Aus verschiedenen Gründen möchte ich nicht mit einer statischen Funktion arbeiten (das hängt mit der späteren Anwendung zusammen).

    Warum kommt es zu dem Fehler?
    Wie kann man eine solche Funktionalität korrekt umsetzen?

    Gruß
    inverterguru



  • @HardwareBastler
    Schau dir mal std::function an.



  • Die Klasse (bzw. das Template) ist für solche Funktionen hilfreich (Danke für den Hinweis), löst aber das Problem nicht. Es läuft letztendlich auf die gleiche Fehlermeldung hinaus.
    Ich habe es folgendermaßen versucht:

    #include <functional>
    
    class FirstClass{
      public:
          int Func(char* d, int x) { return x; }      
    };
    
    class SecondClass{
      public:
          std::function<int(char*,int)> CallBakFunc;
          void Func(std::function<int(char*,int)> CallBak)
          {
              CallBakFunc = CallBak;
              // ...
              char s[10];
              CallBakFunc(s,5);
          }      
    };
    
    FirstClass Fc;
    SecondClass Sc;
    
    int xFunc(char* d, int x) {  return 0; }
    
    int main(int, char**)
    {
        Sc.Func(xFunc);     // Ok
        Sc.Func(Fc.Func); // error: invalid use of non-static member function ‘int FirstClass::Func(char*, int)’
        return 0;
    }
    
    


  • @HardwareBastler joa, das ist eine Memberfunktion. Die braucht eine Instanz auf der die arbeiten kann. Vielleicht ist std::bind das was du suchst



  • @HardwareBastler sagte in Implementieren einer Callback-Funktion:

    invalid use of non-static member function ‘int FirstClass::Func(char*, int)’

    Und eine static int FirstClass::Func(char*, int) funktioniert nicht?



  • @HardwareBastler

    Ich glaube wir stoßen hier auf ein Design-Problem.

    • Greift die Funktion FirstClass::Func auf Klassenattribute zu?
    • Ist SecondClass::CallbackFunc notwendig oder genügt hier einfach nur ein Funktionsparameter?
    • Wird SecondClass::CallbackFunc nur einmal pro Klasseninstanz gesetzt?


  • #include <functional>
    
    class FirstClass{
      public:
          int Func(char* d, int x) { return x; }      
    };
    
    class SecondClass{
      public:
          std::function<int(FirstClass&, char*,int)> CallBakFunc;
          void Func(FirstClass fc, std::function<int(FirstClass&, char*,int)> CallBak)
          {
              CallBakFunc = CallBak;
              // ...
              char s[10];
              CallBakFunc(fc, s,5);
          }      
    };
    
    FirstClass Fc;
    SecondClass Sc;
    
    int xFunc(char* d, int x) {  return 0; }
    
    int main(int, char**)
    {
        //Sc.Func(xFunc);     // NICHT ok, weil xFunc keine Memberfunktion ist
        Sc.Func(Fc, &FirstClass::Func); // OK
        return 0;
    }
    


  • Du kannst den Aufruf in einen Lambda verpacken, in dem du das Objekt fängst. Musst allerdings drauf achten, dass das Lambda länger lebt als das Objekt, das es fängt.

    #include <string>
    #include <functional>
    #include <iostream>
    
    struct MyStruct
    {
       void mem_fun( char* arg1, int arg2 )
       {
          std::cout << arg1 << ", " << arg2 << std::endl;
       }
    };
    
    
    int main()
    {
       MyStruct s;
    
       using Callback_t = std::function<void( char*, int )>;
    
       Callback_t callb = Callback_t( [&]( char* arg1, int arg2 )
       {
          return s.mem_fun( arg1, arg2 );
       } );
       char ch[] = "Hello World";
    
       callb( ch, -1 );
    }


  • Vermutlich ich das wirklich ein Designproblem. Ich beschreibe mal meine Aufgabenstellung etwas ausführlicher.

    Ich möchte eine Klasse schreiben, die verschiedene Berechnungen anstellt. Die Ausgabe erfolgt, je nach Bedarf, an unterschiedliche Klassen. Die Gemeinsamkeit dieser Ausgabeklassen ist, dass jede dieser Klassen eine Funktion in der Form int Func(char*, int) enthält.

    Meine Idee ist es daher eine Klasse zu erstellen, die ungefähr so aussieht:

    class MyClass{
     private:
      *pCb; // Pointer auf Ausgabeklasse
      char s[256]; // Interne Variable
      int v; // Interne Variable
    
     public:
      void AssignPointer(int(*CallBak)(char* dat, int v)) // Initialisieren des Pointers
      {
        pCb = CallBak;
      }
    
      void MachWas(void)
      {
        // … Irgendwelche Berechnungen
        pCb(s,v);
      }
    
      void MachWasAnderes(void)
      {
        // … Irgendwelche Berechnungen
        pCb(s,v);
      }
    };
    

    Das funktioniert solange die CallBak-Funktion entweder eine C-Funktion ist oder diese in der „fremden“ Klasse als static deklariert ist. Die entsprechenden Funktionen in den Ausgabeklassen möchte ich möglichst nicht static deklarieren, da dies wieder zu weiteren Problem führt, wie folgendes einfaches Beispiel zeigt:

    class FirstClass{
     public:
      char s[100];
      int x;
      static int Func(char* d, int x)
      {
        s[0] = d[0]; // error: invalid use of member ‘FirstClass::s’ in static member function
        return x;
      }
    };
    

    Ein weiterer Grund ist, das diese Ausgabeklassen schon existieren und gut getestet sind. Daher möchte ich sie möglichst nicht zu umfangreich modifizieren.
    Momentan löse ich das, indem ich eine C-Funktion dazwischenschalte:

    int Ausgabe(char* s, int i)
    {
      switch(Destination)
      {
        case Sw1: F1.Func(s,i); break;
        case Sw2: F2.Func(s,i); break;
        // …
      }
    }
    

    Die Klassen F1, F2, usw. sind global definiert (was für meine Anwendung ok ist).
    Das müsste eigentlich eleganter gehen?



  • Zitat: "Ich möchte eine Klasse schreiben, die verschiedene Berechnungen anstellt. Die Ausgabe erfolgt, je nach Bedarf, an unterschiedliche Klassen. Die Gemeinsamkeit dieser Ausgabeklassen ist, dass jede dieser Klassen eine Funktion in der Form int Func(char*, int) enthält."

    Bau doch eine (abstrakte?) Oberklasse MyOutput mit der pur virtuellen Ausgabefunktion und leite von dieser deine spezifischen Ausgabeklassen mit den überschriebenen Ausgabefunktion ab. Dem Objekt der Berechnungsklasse übergibst Du dann anstelle der Callback-Funktion ein Objekt der spezifischen Ausgabeklassen (erwartet Oberklasse MyOutput) wo dann die Ausgabefunktion aufgerufen wird.



  • @HardwareBastler

    Würde hier evt. ein Template weiterhelfen?

    #include <iostream>
    
    
    class FirstClassA {
    public:
        int Func(char* d, int x) { 
            std::cout << "A\n";
            return x; 
        }
    };
    
    class FirstClassB {
    public:
        int Func(char* d, int x) {
            std::cout << "B\n";
            return x;
        }
    };
    
    template<typename T>
    class SecondClass {
    public:
        T mCalculationClass;
    
    
        SecondClass(const T& CalculationClass) :
            mCalculationClass(CalculationClass)
        {
    
        }
    
        void Func()
        {
            char s[10];
    
            mCalculationClass.Func(s, 5);
        }
    };
    
    
    int main()
    {
        FirstClassA A;
        FirstClassB B;
        
        SecondClass sca(A);    
        SecondClass scb(B);
    
        sca.Func();
        scb.Func();
        return 0;
    }
    


  • Ja, das ist eine praktikable Lösung.
    In Deinem Beispiel fehlt aber noch der Typ des Templates, richtig muss es heißen:

    ...    
    SecondClass<FirstClassA> sca(A);    
    SecondClass<FirstClassB> scb(B);
    ...
    

    Ich Danke Dir und allen anderen, die sich mit dem Problem beschäftigt haben!



  • @HardwareBastler sagte in Implementieren einer Callback-Funktion:

    In Deinem Beispiel fehlt aber noch der Typ des Templates, richtig muss es heißen:

    Es sollte auch ohne Template Argumente gehen, wenn dein Compiler C++17 unterstützt und aktiviert ist.


Log in to reply