Verwenden einer C++-Klassenmitgliedsfunktion als C-Callback-Funktion

Lesezeit: 7 Minuten

Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
Methoden

Ich habe eine C-Bibliothek, für die eine Callback-Funktion registriert werden muss, um einige Verarbeitungen anzupassen. Typ der Callback-Funktion ist int a(int *, int *).

Ich schreibe C++-Code ähnlich dem folgenden und versuche, eine C++-Klassenfunktion als Callback-Funktion zu registrieren:

class A {
  public:
   A();
   ~A();
   int e(int *k, int *j);
};

A::A()
{
   register_with_library(e)
}

int
A::e(int *k, int *e)
{
  return 0;
}

A::~A() 
{

}

Der Compiler wirft folgenden Fehler:

In constructor 'A::A()',
error:
 argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.

Meine Fragen:

  1. Zunächst einmal ist es möglich, eine C++-Klassenmitgliedsfunktion zu registrieren, wie ich es versuche, und wenn ja, wie? (Ich lese 32,8 bei http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html. Aber meiner Meinung nach löst es das Problem nicht)
  2. Gibt es eine alternative/bessere Möglichkeit, dies anzugehen?

1646646611 744 Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
scharfer Zahn

Sie können dies tun, wenn die Elementfunktion statisch ist.

Nicht statische Elementfunktionen der Klasse A haben einen impliziten ersten Parameter vom Typ class A* was entspricht Das Zeiger. Deshalb konnten Sie sie nur registrieren, wenn die Signatur des Rückrufs auch den ersten Parameter von hatte class A* Art.

  • Jawohl. diese Lösung hat funktioniert. Was mich verwirrt, ist, dass der Compiler keinen Fehler int (A::)(A Intint*)“ passt nicht zu „int ()(int, int*)“

    – Methos

    16. Juni 2009 um 10:31 Uhr

  • Das tat es, aber durch Setzen von (A::), was bedeutet, dass die Funktion Teil der Klasse A ist, was von dort aus den ‘this’-Zeiger impliziert.

    – GManNickG

    16. Juni 2009 um 10:37 Uhr

  • Ich bin nur neugierig … ist das in der Norm vorgeschrieben? Ich habe gerade einen Blick auf den Abschnitt über Klassen geworfen und das hier nicht gefunden. Trotzdem sehr interessant. Ich würde einfach nicht denken, dass jeder Compiler unbedingt nicht-statische Member-Funktionen auf diese Weise behandeln muss.

    – Tom

    16. Juni 2009 um 10:59 Uhr

  • @Methos, zu sagen, dass Member-Funktionen einen impliziten ersten Parameter haben, bedeutet nicht, dass dieser Parameter wirklich existiert. Es bedeutet, dass es konzeptionell da ist.

    – Johannes Schaub – litb

    16. Juni 2009 um 11:00 Uhr

  • @Tom, der Standard nennt es “impliziten Objektparameter”, und es ist vom Typ A& für nicht konstante Elementfunktionen und A const& für konstante Elementfunktionen, A volatile& für volatile … und so weiter. Es ist eine Referenz, während “this” ein Zeiger ist – hauptsächlich wegen der Geschichte. Das Objekt, für das die Mitgliedsfunktion aufgerufen wird, wird als “impliziertes Objektargument” bezeichnet. Der implizite Objektparameter wird zum Zwecke der Überladungsauflösung als versteckter erster Parameter behandelt – aber das alles ist nur konzeptionell, nichts, was wirklich vorhanden sein muss

    – Johannes Schaub – litb

    16. Juni 2009 um 11:02 Uhr

1646646612 511 Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
Anne van Rossum

Sie können dies auch tun, wenn die Member-Funktion nicht statisch ist, aber es erfordert etwas mehr Arbeit (siehe auch C++-Funktionszeiger in C-Funktionszeiger konvertieren):

#include <stdio.h>
#include <functional>

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
   template <typename... Args> 
   static Ret callback(Args... args) {                    
      return func(args...);  
   }
   static std::function<Ret(Params...)> func; 
};

template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

void register_with_library(int (*func)(int *k, int *e)) {
   int x = 0, y = 1;
   int o = func(&x, &y);
   printf("Value: %i\n", o);
}

class A {
   public:
      A();
      ~A();
      int e(int *k, int *j);
};

typedef int (*callback_t)(int*,int*);

A::A() {
   Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
   callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);      
   register_with_library(func);      
}

int A::e(int *k, int *j) {
   return *k - *j;
}

A::~A() { }

int main() {
   A a;
}

Dieses Beispiel ist vollständig in dem Sinne, dass es kompiliert:

g++ test.cpp -std=c++11 -o test

Sie benötigen die c++11 Flagge. Im Code sieht man das register_with_library(func) heißt, wo func ist eine statische Funktion, die dynamisch an die Memberfunktion gebunden ist e.

  • Cool! Ich wollte schon immer wissen, wie man das macht.

    – Jacko

    1. November 2015 um 14:58 Uhr

  • @Jacko. Mmm … das ist ungefähr der Angerufene / Anrufer, der für die Stapelbereinigung verantwortlich ist, nicht wahr? Ich weiß nicht … Ich habe alles über Windows vergessen. 🙂

    – Anne van Rossum

    1. November 2015 um 18:55 Uhr

  • Wie würde man das tun, um threadsicher zu sein? Ich habe die Frage hier gepostet: stackoverflow.com/questions/41198854/…

    – Victor.dMdB

    17. Dezember 2016 um 13:57 Uhr


  • Ihre Antwort wird sehr geschätzt!

    – Dimfred

    13. April 2019 um 17:25 Uhr

  • @AnnevanRossum Ihre Lösung ist großartig, aber ich bin auf ein Problem gestoßen, als ich versucht habe, zwei solcher Rückrufe zu erstellen, und der zweite den ersten überschreibt. Ich habe unter stackoverflow.com/q/66474621/2725742 darüber gepostet, was die minimale Änderung wäre, die erforderlich wäre, um “statische Wrapper” wie diese zu trennen.

    – Benutzer2725742

    4. März 2021 um 12:46 Uhr

1646646613 394 Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
Raoul Supercopter

Das Problem ist, dass Methode != Funktion. Der Compiler wird Ihre Methode in etwa so umwandeln:

int e( A *this, int *k, int *j );

Es ist also sicher, dass Sie es nicht übergeben können, da die Klasseninstanz nicht als Argument übergeben werden kann. Eine Möglichkeit, dies zu umgehen, besteht darin, die Methode statisch zu machen, auf diese Weise hätte sie den guten Typ. Aber es wird keine Klasseninstanz und Zugriff auf nicht statische Klassenmitglieder geben.

Die andere Möglichkeit besteht darin, eine Funktion mit einem statischen Zeiger auf ein beim ersten Mal initialisiertes A zu deklarieren. Die Funktion leitet den Aufruf nur an die Klasse um:

int callback( int *j, int *k )
{
    static A  *obj = new A();
    a->(j, k);
}

Dann können Sie die Callback-Funktion registrieren.

  • Was ist eine ‘Methode’ in C++? Dieses Wort kommt im C++-Standard kein einziges Mal vor.

    – Aconcagua

    13. April 2019 um 12:38 Uhr


  • @Aconcagua, ich würde mir vorstellen, dass Sie es wissen, aber hier ist eine Antwort auf Ihre Frage: stackoverflow.com/questions/8596461/…

    – Alexis Wilke

    30. April 2019 um 18:07 Uhr

  • Ein Funktionsmember (“Methode”) ist definitiv eine Funktion. Die Tatsache, dass es (tatsächlich) einen zusätzlichen Parameter gibt, macht es nicht zu einem Objekt ohne Funktion.

    – Alexis Wilke

    30. April 2019 um 18:09 Uhr

  • @AlexisWilke Viel wichtiger sind die ersten beiden Bemerkungen zur angegebenen Antwort. Zusätzlich würde der zweite Absatz (“Austauschbarkeit”) “Funktion != Funktion” implizieren. Es mag auf den ersten Blick wie Haarspalterei aussehen, aber ich musste auf die harte Tour lernen (leichte Missverständnisse führen zu schweren Fehlern), wie wichtig klare Definitionen sind. Daraus leiten sich zwei wichtige Regeln ab: 1. Verwenden Sie keine Begrifflichkeiten, die nicht eindeutig definiert sind! 2. Verwenden Sie keine neuen Definitionen parallel zu bestehenden.

    – Aconcagua

    1. Mai 2019 um 6:37 Uhr

  • Im a->(j, k);haben Sie die Eingabe verpasst e?

    – Alexis Wilke

    2. Mai 2019 um 3:55 Uhr

Nun … wenn Sie auf einer Win32-Plattform sind, gibt es immer den bösen Thunking-Weg …

Thunking in Win32: Vereinfachen von Rückrufen an nicht statische Elementfunktionen

Es ist eine Lösung, aber ich empfehle nicht, sie zu verwenden.
Es hat eine gute Erklärung und es ist schön zu wissen, dass es existiert.

1646646613 71 Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
cibercitizen1

In dieser Lösung haben wir eine Vorlagenklasse mit der statischen Methode, die der “c-Funktion” als Rückruf übergeben werden soll. Diese Klasse enthält ein “gewöhnliches” Objekt (mit einer Mitgliedsfunktion namens callback(), die schließlich aufgerufen wird).

Sobald Ihre Klasse (hier A) definiert ist, kann sie einfach verwendet werden:

int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

Vollständiges Beispiel:

#include <iostream>

// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
  static inline HeldObjectType object;

  static void callback( ) {
    object.callback();
  } // ()

  HeldObjectType &  operator() ( ) {
    return object;
  }

  Holder( HeldObjectType && obj )
  {
    object = obj;
  }

  Holder() = delete;

}; // class

// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);

// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
  f();
} // ()

// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
  std::cout << "I'm fun\n";
} // 

// ----------------------------------------------------------
// 
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
// 
// ----------------------------------------------------------
class A {
private:
  int n;

public:

  A(  ) : n( 0 ) { }

  A( int a, int b ) : n( a+b ) { }

  void callback( ) {
    std::cout << "A's callback(): " << n << "\n";
  }

  int getN() {
    return n;
  }

}; // class

// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

1646646614 496 Verwenden einer C Klassenmitgliedsfunktion als C Callback Funktion
PaulJWilliams

Das Problem bei der Verwendung einer Member-Funktion besteht darin, dass sie ein Objekt benötigt, auf das sie reagieren kann – und C weiß nichts über Objekte.

Am einfachsten wäre folgende Vorgehensweise:

//In a header file:
extern "C" int e(int * k, int * e);

//In your implementation: 
int e(int * k, int * e) { return 0; }

964940cookie-checkVerwenden einer C++-Klassenmitgliedsfunktion als C-Callback-Funktion

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy