Home > Software engineering >  Interfacing external C code with duplicate method names using Cython
Interfacing external C code with duplicate method names using Cython

Time:05-23

I want to provide a native interface to some C code from my Python code. For this I decided on Cython.

In this specific case, I have some C files which have duplicate names, resulting in compilation issues. Is there any way I can circumvent this? The solution from the official documentation (https://cython.readthedocs.io/en/latest/src/userguide/external_C_code.html#resolving-naming-conflicts-c-name-specifications) does not seem to be sufficient.

The following example has been stripped down to demonstrate the issue, while my actual use case is for a larger third-party library. In the best case, I can avoid having multiple *.so files with similar content. I might need to use the different definitions of the same method during runtime.


Example

# File setup.py
from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("my_test.pyx", language_level=3)
)
# File my_test.pyx
from binding cimport add as _add, subtract as _subtract, module1_main as _main

print('Hello World')


def add(a, b):
    return _add(a, b)


def subtract(a, b):
    return _subtract(a, b)


def main():
    return _main()
# File binding.pxd
cdef extern from "module1.c":
    int add (int a, int b)
    int subtract (int a, int b)
    int module1_main "main" ()


cdef extern from "module2.c":
    int module2_main "main" ()
// File module1.c
#include <stdio.h>

static int add(int a, int b) {
    return a   b;
}


static int subtract(int a, int b) {
    return a - b;
}


int main() {
    printf("Result: %d", add(40, 2));
    return 0;
}
// File module2.c
#include <stdio.h>


int main() {
    printf("Hello from module 2");
    return 0;
}

Error message

(venv) user@host ~/path/to/directory $ python setup.py build_ext --inplace
Compiling my_test.pyx because it changed.
[1/1] Cythonizing my_test.pyx
running build_ext
building 'my_test' extension
x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -fPIC -I. "-I/home/user/path/to/directory/venv/include" -I/usr/include/python3.8 -c my_test.c -o build/temp.linux-x86_64-cpython-38/my_test.o
In file included from my_test.c:716:
module2.c:4:5: error: redefinition of ‘main’
    4 | int main() {
      |     ^~~~
In file included from my_test.c:715:
module1.c:13:5: note: previous definition of ‘main’ was here
   13 | int main() {
      |     ^~~~
error: command '/usr/bin/x86_64-linux-gnu-gcc' failed with exit code 1

CodePudding user response:

  • Generally static is used to restrict functions to a single complication unit, and so you need to modify your C files and make the duplicate functions static. Defining the functions as static would allow you to create separate Cython modules wrapping the function, but not allow you to use the conflicting functions in the same modules like you're trying to do here. The flip-side of doing this is you must include the function definitions in your Cython file - you can't just include a header and link it later.

  • main has a special meaning in C (it defines a starting point for a full program), so you almost certainly don't want C functions called main.

  • The Cython "cname" feature you link int module2_main "main" () doesn't (and will never!) solve your problem. It essentially lets you refer to main as module2_main in your Cython code only. It still gets translated to C code main, and so your conflicting definitions are still a problem.

  • This is an issue that you have to solve in C (probably by renaming the functions). If the C libraries are truly external and you can't modify them then you're pretty much stuck.


Quick addendum with a little bit of testing:

  • You can wrap a function called main() with Cython;
  • If you want to wrap two different functions called with the same name then they must go in different Cython modules.
  • Because of the way that Cython imports modules dynamically, there's no conflict between the two modules at run-time.

But this is mostly a C/linker problem...

  • Related