Necromancing.
I think the answers to date are a bit unclear.
Let's make an example:
Suppose you have an array of pixels (an array of ARGB values int8_t)
Now you want to create a PNG. To do this, you call the toJpeg function
bool ok = toJpeg(writeByte, pixels, width, height);
where writeByte is a callback function
void writeByte(unsigned char oneByte) { fputc(oneByte, output); }
The problem here is: FILE * output must be a global variable.
Very bad if you are in a multi-threaded environment (for example, an http server).
Therefore, you need some way to draw the output of a non-global variable, while maintaining the signature of the callback.
The immediate solution that comes to mind is a closure that we can emulate using a class with a member function.
class BadIdea { private: FILE* m_stream; public: BadIdea(FILE* stream) { this->m_stream = stream; } void writeByte(unsigned char oneByte){ fputc(oneByte, this->m_stream); } };
And then do
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);
However, contrary to expectations, this does not work.
The reason is that C ++ member functions are implemented as C # extension functions.
So do you have
class/struct BadIdea { FILE* m_stream; }
as well as
static class BadIdeaExtensions { public static writeByte(this BadIdea instance, unsigned char oneByte) { fputc(oneByte, instance->m_stream); } }
Therefore, when you want to call writeByte, you need to pass not only the writeByte address, but also the address of the BadIdea instance.
So when you have a typedef for the writeByte procedure, and it looks like this
typedef void (*WRITE_ONE_BYTE)(unsigned char);
And you have a writeJpeg signature that looks like this
bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t width, uint32_t height)) { ... }
it is fundamentally impossible to pass a two-address member function to a pointer to a unicast function (without changing writeJpeg), and there is no way around this.
The next best thing you can do in C ++ is to use a lambda function:
FILE *fp = fopen(filename, "wb"); auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp); }; bool ok = TooJpeg::writeJpeg(lambda, image, width, height);
However, since lambda does nothing more than pass an instance to a hidden class (for example, "BadIdea" -class), you need to change the signature of writeJpeg.
The advantage of lambda over manual class is that you just need to change one typedef
typedef void (*WRITE_ONE_BYTE)(unsigned char);
at
using WRITE_ONE_BYTE = std::function<void(unsigned char)>;
And then you can leave everything else untouched.
You can also use std :: bind
auto f = std::bind(&BadIdea::writeByte, &foobar);
But this, behind the scenes, just creates a lambda function that also requires a change in typedef.
So no, there is no way to pass a member function to a method that requires a static function pointer.
But lambdas are an easy way to go if you have control over the source.
Otherwise, you're out of luck.
There is nothing you can do with C ++.
Remarks:
std :: function requires #include <functional>
However, since C ++ also allows you to use C, you can do this with libffcall in simple C, if you don't mind binding the dependency.
Download libffcall from GNU (at least in Ubuntu, do not use the distribution package - it does not work), unzip it.
./configure make make install gcc main.c -l:libffcall.a -o ma
main.c:
#include <callback.h> // this is the closure function to be allocated void function (void* data, va_alist alist) { int abc = va_arg_int(alist); printf("data: %08p\n", data); // hex 0x14 = 20 printf("abc: %d\n", abc); // va_start_type(alist[, return_type]); // arg = va_arg_type(alist[, arg_type]); // va_return_type(alist[[, return_type], return_value]); // va_start_int(alist); // int r = 666; // va_return_int(alist, r); } int main(int argc, char* argv[]) { int in1 = 10; void * data = (void*) 20; void(*incrementer1)(int abc) = (void(*)()) alloc_callback(&function, data); // void(*incrementer1)() can have unlimited arguments, eg incrementer1(123,456); // void(*incrementer1)(int abc) starts to throw errors... incrementer1(123); // free_callback(callback); return EXIT_SUCCESS; }
And if you use CMake, add the linker library after add_executable
add_library(libffcall STATIC IMPORTED) set_target_properties(libffcall PROPERTIES IMPORTED_LOCATION /usr/local/lib/libffcall.a) target_link_libraries(BitmapLion libffcall)
or you can just dynamically link libffcall
target_link_libraries(BitmapLion ffcall)
Remarks:
You might want to include the libffcall headers and libraries or create a cmake project with the contents of libffcall.