死灵法师。
我认为到目前为止的答案还不清楚。
让我们举个例子:
假设您有一个像素数组(ARGB int8_t值数组)
int8_t* pixels = new int8_t[1024*768*4];
现在您要生成一个PNG。为此,您可以将函数调用为Jpeg
bool ok = toJpeg(writeByte, pixels, width, height);
其中writeByte是一个回调函数
void writeByte(unsigned char oneByte)
{
fputc(oneByte, output);
}
这里的问题是:FILE *输出必须是全局变量。
如果您在多线程环境中(例如,http服务器),则非常糟糕。
因此,您需要某种方法来使输出成为非全局变量,同时保留回调签名。
想到的直接解决方案是闭包,我们可以使用带有成员函数的类进行模拟。
class BadIdea {
private:
FILE* m_stream;
public:
BadIdea(FILE* stream) {
this->m_stream = stream;
}
void writeByte(unsigned char oneByte){
fputc(oneByte, this->m_stream);
}
};
然后做
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);
但是,与预期相反,这是行不通的。
原因是,C ++成员函数有点像C#扩展函数那样实现。
所以你有了
class/struct BadIdea
{
FILE* m_stream;
}
和
static class BadIdeaExtensions
{
public static writeByte(this BadIdea instance, unsigned char oneByte)
{
fputc(oneByte, instance->m_stream);
}
}
因此,当您要调用writeByte时,不仅需要传递writeByte的地址,还需要传递BadIdea实例的地址。
因此,当您具有writeByte过程的typedef时,它看起来像这样
typedef void (*WRITE_ONE_BYTE)(unsigned char);
而且您有一个看起来像这样的writeJpeg签名
bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t
width, uint32_t height))
{ ... }
从根本上讲,不可能将两个地址的成员函数传递给一个地址的函数指针(而无需修改writeJpeg),并且没有办法解决。
在C ++中可以做的第二件事是使用lambda函数:
FILE *fp = fopen(filename, "wb");
auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp); };
bool ok = TooJpeg::writeJpeg(lambda, image, width, height);
但是,由于lambda与将实例传递给隐藏类(例如“ BadIdea”类)没有什么不同,因此您需要修改writeJpeg的签名。
与手动类相比,lambda的优势在于,您只需要更改一个typedef
typedef void (*WRITE_ONE_BYTE)(unsigned char);
至
using WRITE_ONE_BYTE = std::function<void(unsigned char)>;
然后,您可以保留所有其他内容。
您也可以使用std :: bind
auto f = std::bind(&BadIdea::writeByte, &foobar);
但这只是在后台创建了一个lambda函数,然后还需要更改typedef。
因此,没有办法,无法将成员函数传递给需要静态函数指针的方法。
但是只要您对源代码有控制权,lambdas就是简单的解决方法。
否则,你很不幸。
C ++是您无能为力的。
注意:
std :: function需要#include <functional>
但是,由于C ++也允许您使用C,因此,如果您不介意链接依赖项,则可以在纯C中使用libffcall来实现。
从GNU下载libffcall(至少在ubuntu上,不要使用发行版提供的包-它已损坏),解压缩。
./configure
make
make install
gcc main.c -l:libffcall.a -o ma
main.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;
}
如果使用CMake,请在add_executable之后添加链接器库
add_library(libffcall STATIC IMPORTED)
set_target_properties(libffcall PROPERTIES
IMPORTED_LOCATION /usr/local/lib/libffcall.a)
target_link_libraries(BitmapLion libffcall)
或者你可以动态链接libffcall
target_link_libraries(BitmapLion ffcall)
注意:
您可能要包括libffcall标头和库,或者使用libffcall的内容创建一个cmake项目。