Nekromantie.
Ich denke, die bisherigen Antworten sind etwas unklar.
Machen wir ein Beispiel:
Angenommen, Sie haben ein Array von Pixeln (Array von ARGB int8_t-Werten)
int8_t* pixels = new int8_t[1024*768*4];
Jetzt möchten Sie ein PNG generieren. Dazu rufen Sie die Funktion toJpeg auf
bool ok = toJpeg(writeByte, pixels, width, height);
Dabei ist writeByte eine Rückruffunktion
void writeByte(unsigned char oneByte)
{
fputc(oneByte, output);
}
Das Problem hier: Die Ausgabe von FILE * muss eine globale Variable sein.
Sehr schlecht, wenn Sie sich in einer Multithread-Umgebung befinden (z. B. einem http-Server).
Sie benötigen also eine Möglichkeit, die Ausgabe zu einer nicht globalen Variablen zu machen, während die Rückrufsignatur beibehalten wird.
Die unmittelbare Lösung, die in den Sinn kommt, ist ein Abschluss, den wir mithilfe einer Klasse mit einer Elementfunktion emulieren können.
class BadIdea {
private:
FILE* m_stream;
public:
BadIdea(FILE* stream) {
this->m_stream = stream;
}
void writeByte(unsigned char oneByte){
fputc(oneByte, this->m_stream);
}
};
Und dann tun
FILE *fp = fopen(filename, "wb");
BadIdea* foobar = new BadIdea(fp);
bool ok = TooJpeg::writeJpeg(foobar->writeByte, image, width, height);
delete foobar;
fflush(fp);
fclose(fp);
Dies funktioniert jedoch wider Erwarten nicht.
Der Grund dafür ist, dass C ++ - Mitgliedsfunktionen wie C # -Erweiterungsfunktionen implementiert sind.
Also hast du
class/struct BadIdea
{
FILE* m_stream;
}
und
static class BadIdeaExtensions
{
public static writeByte(this BadIdea instance, unsigned char oneByte)
{
fputc(oneByte, instance->m_stream);
}
}
Wenn Sie also writeByte aufrufen möchten, müssen Sie nicht nur die Adresse von writeByte übergeben, sondern auch die Adresse der BadIdea-Instanz.
Wenn Sie also ein typedef für die writeByte-Prozedur haben und es so aussieht
typedef void (*WRITE_ONE_BYTE)(unsigned char);
Und Sie haben eine writeJpeg-Signatur, die so aussieht
bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t
width, uint32_t height))
{ ... }
Es ist grundsätzlich unmöglich, eine Elementfunktion mit zwei Adressen an einen Funktionszeiger mit einer Adresse zu übergeben (ohne writeJpeg zu ändern), und daran führt kein Weg vorbei.
Das nächstbeste, was Sie in C ++ tun können, ist die Verwendung einer Lambda-Funktion:
FILE *fp = fopen(filename, "wb");
auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp); };
bool ok = TooJpeg::writeJpeg(lambda, image, width, height);
Da Lambda jedoch nichts anderes tut, als eine Instanz an eine versteckte Klasse (wie die "BadIdea" -Klasse) zu übergeben, müssen Sie die Signatur von writeJpeg ändern.
Der Vorteil von Lambda gegenüber einer manuellen Klasse besteht darin, dass Sie nur ein typedef ändern müssen
typedef void (*WRITE_ONE_BYTE)(unsigned char);
zu
using WRITE_ONE_BYTE = std::function<void(unsigned char)>;
Und dann können Sie alles andere unberührt lassen.
Sie können auch std :: bind verwenden
auto f = std::bind(&BadIdea::writeByte, &foobar);
Dies erzeugt jedoch hinter den Kulissen nur eine Lambda-Funktion, die dann auch die Änderung in typedef benötigt.
Nein, es gibt keine Möglichkeit, eine Elementfunktion an eine Methode zu übergeben, für die ein statischer Funktionszeiger erforderlich ist.
Aber Lambdas sind der einfache Weg, vorausgesetzt, Sie haben die Kontrolle über die Quelle.
Ansonsten hast du kein Glück.
Mit C ++ können Sie nichts anfangen.
Hinweis: Die
Funktion std :: erfordert#include <functional>
Da Sie in C ++ auch C verwenden können, können Sie dies mit libffcall in einfachem C tun, wenn Sie nichts dagegen haben, eine Abhängigkeit zu verknüpfen.
Laden Sie libffcall von GNU herunter (zumindest unter Ubuntu, verwenden Sie nicht das von der Distribution bereitgestellte Paket - es ist kaputt), entpacken Sie es.
./configure
make
make install
gcc main.c -l:libffcall.a -o ma
Haupt c:
#include <callback.h>
void function (void* data, va_alist alist)
{
int abc = va_arg_int(alist);
printf("data: %08p\n", data);
printf("abc: %d\n", abc);
}
int main(int argc, char* argv[])
{
int in1 = 10;
void * data = (void*) 20;
void(*incrementer1)(int abc) = (void(*)()) alloc_callback(&function, data);
incrementer1(123);
return EXIT_SUCCESS;
}
Wenn Sie CMake verwenden, fügen Sie die Linkerbibliothek nach add_executable hinzu
add_library(libffcall STATIC IMPORTED)
set_target_properties(libffcall PROPERTIES
IMPORTED_LOCATION /usr/local/lib/libffcall.a)
target_link_libraries(BitmapLion libffcall)
oder Sie können libffcall einfach dynamisch verknüpfen
target_link_libraries(BitmapLion ffcall)
Hinweis:
Möglicherweise möchten Sie die libffcall-Header und -Bibliotheken einschließen oder ein cmake-Projekt mit dem Inhalt von libffcall erstellen.