将函数指针作为API接口传递到已编译的库

Passing function pointers as an API interface to a compiled library

本文关键字:编译 接口 指针 函数 API      更新时间:2023-10-16

最静止堆栈交换,

我正在为核磁共振扫描仪编程。我不会深入太多的背景,但我在访问多少代码方面受到了相当大的限制,而且设置的方式……不太理想。我的情况如下:

  • 有一个用C++编写的大型库。它最终进行"代码转换"(以最糟糕的方式),写出完成任务的FPGA组件。它为"userland"提供了一组函数,这些函数被翻译成(通过预处理器宏和黑魔法的混合)16位和32位单词的长字符串。这样做的方式很容易发生缓冲区溢出,而且通常会发生溢出*
  • 然后,FPGA组件通过一个光荣的串行链路连接到相关的电子设备,电子设备执行它(进行扫描),并再次返回数据进行处理
  • 程序员应该使用库提供的函数来完成他们的工作,在与标准库链接的C(而不是C++)函数中。不幸的是,就我而言,我需要扩建图书馆
  • 在你在代码中写doSomething()和相关的库函数实际执行它之间,有一个相当复杂的预处理器替换和标记化、调用和(通常)的事情发生的链。我想我在某种程度上已经弄清楚了,但这基本上意味着我对任何东西的范围都没有真正的了解

简而言之,我的问题是:

  • 在一个方法的中间,在一个大斑点中成千上万行代码的黑暗角落里,我几乎无法控制,随着god-knows-what variable scopeing的进行,我需要:
    • 将此方法扩展为将函数指针(指向userland函数)作为参数,但是
    • 让这个在编译库之后编写的userland函数可以访问它出现的方法范围的本地变量,以及调用它的(C)函数中的变量

这似乎是记忆管理的一个绝对的泥潭,我想我应该在这里寻求在这种情况下的"最佳实践",因为我可能会遇到很多微妙的问题,而其他人可能有很多相关的智慧可以传授。调试系统是一场噩梦,我还没有得到扫描仪制造商的任何支持。

我计划如何进行的简要概述如下:

在.cpp库中:

/* In something::something() /*
/* declare a pointer to a function */
    void (*fp)(int*, int, int, ...);
/* by default, the pointer points to a placeholder at compile time*/
    fp = &doNothing(...);
...
/* At the appropriate time, point the pointer to the userland function, whose address is supplied as an argument to something(): /*
    fp= userFuncPtr;
/* Declare memory for the user function to plonk data into */ 
    i_arr_coefficients = (int) malloc(SOMETHING_SENSIBLE);
/* Create a pointer to that array for the userland function */ 
    i_ptr_array=&i_arr_coefficients[0];
/* define a struct of pointers to local variables for the userland function to use*/
 ptrStrct=createPtrStruct(); 
/* Call the user's function: */
fp(i_ptr_array,ptrStrct, ...);
CarryOnWithSomethingElse();

占位符函数的目的是在用户函数没有链接的情况下保持正常运行。我知道它可以用#DEFINE代替,但编译器的聪明或愚蠢可能会导致奇怪的(至少在我无知的头脑中)行为。

在userland函数中,我们会有这样的东西:

void doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...) { 
    double a=*ptrStrct.a;
    double b=*ptrStrct.b;
    double c=*localVariableAddresses.c;
    double d=doMaths(a, b, c);
    /* I.e. do maths using all of these numbers we've got from the different sources */
    storeData(i_ptr_array, d);
    /* And put the results of that maths where the C++ method can see it */
}
...
something(&doUsefulThings(i_ptr_array, ptrStrct, localVariableAddresses, ...), ...); 
...

如果这像泥一样清澈,请告诉我!非常感谢你的帮助。顺便说一句,我真诚地希望有人能制造一个开放的硬件/源代码MRI系统。

*顺便说一句,这是制造商最初阻止我们修改大型库的主要理由!

您可以完全访问C代码。您对C++库代码的访问权限有限。C代码定义了"doUsefullthings"函数。从C代码调用"Something"函数(C++类/函数),函数指针指向"doUseFullThings"作为参数。现在控件转到C++库。在这里,各种参数被分配内存并初始化。然后使用这些参数调用"doUseFullThings"。在这里,控制权转移回C代码。简而言之,主程序(C)调用库(C++),库调用C函数。

其中一个要求是"userland函数应该可以从调用它的C代码访问本地变量"。当你调用"某物"时,你只给出了"doUseFullThings"的地址。没有"something"的参数/自变量可以捕获局部变量的地址。因此"doUseFullThings"无法访问这些变量。

malloc语句返回指针。这件事没有得到妥善处理。(可能你是想给我们概述一下)。你一定要小心把它放在某个地方。

由于这是C和C++代码的混合体,因此很难使用RAII(处理分配的内存)、完美转发(避免复制变量)、Lambda函数(访问本地变量包)等。在这种情况下,您的方法似乎是可行的。