I am working with a C extension to Python that dynamically loads a third party library and calls its functions. At one point the third party changed one of their function signatures from foo(int a, int b)
to foo(int a)
, and I am struggling with how to provide a compatability layer.
In Python I can inspect the version of the third party library and pass that down to the C functions. There I've tried to branch like:
if (version >= 1) {
foo(42);
} else {
foo(42, 42);
}
While also providing a declaration like void foo(int a, ...)
This compiles but segfaults at runtime, likely because the space being allocated for a variadic argument is not the same as an extra argument would be.
I have also tried using the newer declaration of foo(int a)
but that still yields too few arguments to 'foo'
during the compilation stage. The older definition won't work here either.
Are there any strategies I can employ to have this code compile and perform the correct behavior at runtime?
CodePudding user response:
You can declare the function with one type and, when calling the other type, convert its address to a pointer to the type the function is actually defined with:
void foo(int a, int b);
void bar(int version)
{
if (version >= 1) {
((void (*)(int)) &foo)(42);
} else {
foo(42, 42);
}
}
I have shown the &
explicitly to make it clear we are taking a pointer to a function, converting it to a pointer to a different type, and using the converted pointer to call the function. However, you can omit the &
, as functions, like arrays, are automatically converted to pointers when needed.
That includes when calling functions, and the function call operation actually takes a pointer, not a function designator. cos(x)
is actually (&cos)(x)
, due to the automatic conversion. That is why we do not need to use *
with the pointer above when making the function call.
This pushes the behavior defined by the C standard a little, but it should be fine in typical C implementations. Conversion of function pointers to other types of function pointers is defined, and calling a function with the type it is actually defined with is of course defined. The questionable part is that this function will be declared with one type, in void foo(int a, int b);
, even though its actually definition is another type. However, since the actual definition is out of view of the C compiler, the compiler’s behavior cannot be affected by that. There could only be a problem in an exotic C implementation that used different representations for pointers to different types of functions, which could cause the representation supplied by the linker for the declared foo
to differ from what the compiler expects.