我正在使用Python的C扩展,该扩展动态加载第三方库并调用其函数。有一次,第三方将他们的一个函数签名从foo(int a, int b)
更改为foo(int a)
,我正在努力解决如何提供兼容层的问题。
在Python中,我可以检查第三方库的版本,并将其传递给C函数。在那里,我试着像一样进行分支
if (version >= 1) {
foo(42);
} else {
foo(42, 42);
}
同时还提供类似void foo(int a, ...)
的声明
这在运行时编译但segfault,可能是因为为可变参数分配的空间与额外参数不同。
我也尝试过使用foo(int a)
的更新声明,但在编译阶段仍然会生成too few arguments to 'foo'
。旧的定义在这里也不起作用。
有什么策略可以让我在运行时编译并执行正确的行为吗?
您可以用一种类型声明函数,并且在调用另一种类型时,将其地址转换为指向函数实际定义的类型的指针:
void foo(int a, int b);
void bar(int version)
{
if (version >= 1) {
((void (*)(int)) &foo)(42);
} else {
foo(42, 42);
}
}
我已经显式地展示了&
,以表明我们正在获取一个指向函数的指针,将其转换为指向不同类型的指针,并使用转换后的指针来调用该函数。但是,您可以省略&
,因为函数(如数组(会在需要时自动转换为指针。
这包括在调用函数时,函数调用操作实际上采用指针,而不是函数指示符。由于自动转换,cos(x)
实际上是(&cos)(x)
。这就是为什么我们在进行函数调用时不需要将*
与上面的指针一起使用。
这稍微推动了C标准定义的行为,但在典型的C实现中应该是可以的。定义了函数指针到其他类型函数指针的转换,当然也定义了用实际定义的类型调用函数。值得怀疑的是,这个函数将在void foo(int a, int b);
中用一种类型声明,尽管它的实际定义是另一种类型。然而,由于C编译器看不到实际的定义,因此编译器的行为不会受到影响。只有在使用不同表示形式作为指向不同类型函数的指针的奇异C实现中才会出现问题,这可能会导致链接器为声明的foo
提供的表示形式与编译器预期的不同。