0

I have a Cython project with the following modules:

CythonModule.pyx:

cdef int CPyFuncSwo():
    print("Do some stuff in cython")
    return 0

cdef public void PyFuncSwo()
    CPyFuncSwo()
    print("Do some stuff but in pure python only")

CythonModule.pxd:


cdef int CPyFuncSwo()

Setup.py:

from distutils.core import setup, Extension
from Cython.Build import cythonize

ext_modules = [
    Extension(
        "CythonModule",
        sources=["CythonModule.pyx",],
    ),
]

setup(
    name="CythonModule",
    ext_modules=cythonize(ext_modules),
    compiler_directives={'language_level': 3, "unraisable_tracebacks": True}
)

run.py:

import os
import sys
from CythonModule import PyFuncSwo
sys.path.insert(0, os.path.abspath("."))


if __name__ == '__main__':
    PyFuncSwo()

This works perfectly fine and when I run python setup.py build_ext --inplace && python run.py, the code compiles and runs while also giving me a new CythonModule.cp39-win_amd64.pyd file. Now I need to call PyFuncSwo() from the .pyd module in a pure C/C++ code. Surfing through the web, I came across the following example:

RunCythonInC.c:

#include <windows.h>
#include <stdio.h>

typedef (add_proc)(int a, int b);
int main(){
    add_proc *add;
    HANDLE h_dll;
    int a = 1, b = 2, c;
    h_dll = LoadLibrary("CythonModule.cp39-win_amd64.pyd");
    if(h_dll)
      {
        add = GetProcAddress(h_dll, "PyFuncSwo");
        if(add)
        {
          c = add(a, b); /*Explicit Call*/
        }
        else
        {
          printf("PyFuncSwo not found in CythonModule.cp39-win_amd64.pyd");
        }
        FreeLibrary(h_dll);
      }
      else
      {
        printf("Unable to load CythonModule.cp39-win_amd64.pyd");
        exit(-1);
      }
    return 0;
}

Compiling this code with gcc as gcc -g RunCythonInC.c -o RunCythonInC.exe compiles with the following warnings, but when I run RunCythonInC.exe, I always get PyFuncSwo not found in CythonModule.cp39-win_amd64.pyd.

I also changed GetProcAddress(h_dll, "PyFuncSwo") to GetProcAddress(h_dll, "CPyFuncSwo") but am I still getting the same error. Other things I tried was calling cython functions with an _ as GetProcAddress(h_dll, "_PyFuncSwo") but still got the same result. Can anyone tell me, how do I call my cython functions from the .pyd module in a pure C code?

Update:

As per the Cython's documentation Here, it seems that the proper way to declare cython functions in C is to define the cython function type as public and build it. This will generate a "CythonModule.h" header file, which can be included in your C code and then can call the cython functions.

following this instruction, I came up with the following C code:

run.c:

#include <windows.h>
#include <stdio.h>
#include <Python.h>
#include "CythonModule.h"

int main(){
    PyImport_AppendInittab("PyFuncSwo", PyInit_CythonModule);
    Py_Initialize();
    PyImport_ImportModule("PyFuncSwo");
    return 0;
}

and changed def PyFuncSwo() to cdef public void PyFuncSwo().

Now I get the following errors:

c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:\Users\username\AppData\Local\Temp\ccaMLv6A.o:run.c:(.rdata$.refptr.PyInit_CythonModule[.refptr.PyInit_CythonModule]+0x0): undefined reference to `PyInit_CythonModule'
collect2.exe: error: ld returned 1 exit status

CythonModule.h:

/* Generated by Cython 0.29.23 */

#ifndef __PYX_HAVE__CythonModule
#define __PYX_HAVE__CythonModule

#include "Python.h"

#ifndef __PYX_HAVE_API__CythonModule

#ifndef __PYX_EXTERN_C
  #ifdef __cplusplus
    #define __PYX_EXTERN_C extern "C"
  #else
    #define __PYX_EXTERN_C extern
  #endif
#endif

#ifndef DL_IMPORT
  #define DL_IMPORT(_T) _T
#endif

__PYX_EXTERN_C void __pyx_f_13CythonModule_PyFuncSwo(void);

#endif /* !__PYX_HAVE_API__CythonModule */

/* WARNING: the interface of the module init function changed in CPython 3.5. */
/* It now returns a PyModuleDef instance instead of a PyModule instance. */

#if PY_MAJOR_VERSION < 3
PyMODINIT_FUNC initCythonModule(void);
#else
PyMODINIT_FUNC PyInit_CythonModule(void);
#endif

#endif /* !__PYX_HAVE__CythonModule */

sdfdf
  • 3
  • 3

1 Answers1

1

Your immediate problem is symbol visibility. The functions need to be cdef public and possibly need manually exporting on Windows.

However

What you're trying to do is wrong and is unlikely to work.

Cython does not create standalone C functions. Instead it generates Python modules. Cython functions have two important prerequisites:

  1. The Python interpreter is running.
  2. The module import function for the Cython module must have been called.

If these prerequisites aren't met the functions are likely to crash in unexpected and confusing ways.

The correct way of calling Cython functions from C is covered in the Cython documentation and does not involve GetProcAddress.

Edit: Full example

I kept your CythonModule.pyx and CythonModule.pxd almost as is (I had to add a colon to the line cdef public void PyFuncSwo().

I compiled those to C with cython CythonModule.pyx.

I modified run.c slightly to remove windows.h since I'm not using Windows and to add PyFuncSwo()

#include <stdio.h>
#include <Python.h>
#include "CythonModule.h"

int main(){
    PyImport_AppendInittab("PyFuncSwo", PyInit_CythonModule);
    Py_Initialize();
    PyImport_ImportModule("PyFuncSwo");
    PyFuncSwo();
    return 0;
}

Note that I use PyFuncSwo() - the mangled version of the name is different on earlier Cython versions but is fixed in both the Cython 3 pre-release and the 0.29.33.

I compile this with gcc python3-config --cflags --ldflags --embed CythonModule.c run.c -o prog (Note on Linux, not Windows)

and running prog gives the output

Do some stuff in cython
Do some stuff but in pure python only

(although in reality the output is wrong - both print statements execute in Cython-compiled code).

DavidW
  • 29,336
  • 6
  • 55
  • 86
  • appologies for late response, but I tried your suggestion and followed the documentation, but I am still having a differnt issue, can you please look at the question "Update" section? – sdfdf Apr 19 '23 at 22:51
  • You need to make sure that you're compiling "Cython module.c" so an .o file and linking that with your main program – DavidW Apr 20 '23 at 06:44
  • DavidW, would be able to provide an example of run.c code calling `PyFuncSwo` from my cythonmodule.pyd for a python3.9? I was able to successfully link CythonModule.c to run.c but executing run.exe doesn't call the `PyFuncSwo` function. my gcc params: `gcc -o run.exe run.c CythonModule.c -IC:\Users\username\AppData\Local\Programs\Python\Python39\include -IC:\Users\username\A ppData\Local\Programs\Python\Python39\include -LC:\Users\username\AppData\Local\Programs\Python\Python39\libs -lpython39` – sdfdf Apr 20 '23 at 20:54
  • I think you just need to call `__pyx_f_13CythonModule_PyFuncSwo` after the module import. (Cython has managed the name a little, which is a bug that I think is fixed in the 3.0 beta version). Don't have time to test it right now I'm afraid but will be able to look in a few days – DavidW Apr 21 '23 at 05:44
  • I tried calling `__pyx_f_13CythonModule_PyFuncSwo`, but still nothing happened, would really appreciate if you test it any time you get a change – sdfdf Apr 21 '23 at 23:24
  • See the edit - it works for me as described – DavidW Apr 23 '23 at 14:07
  • ufff, in windows when I call `PyFuncSwo()` after `PyImport_importModule("PyFuncSwo")`, it says undefined reference: `C:\Users\username\AppData\Local\Temp\cchNT6Gi.o:run.c:(.text+0x3e): undefined reference to `PyFuncSwo'` Also, I don't have `python3-config --cflags --ldflags --embed` in windows gcc. My cython version is `0.29.34` – sdfdf Apr 23 '23 at 22:39
  • It seems that they still havent fixed the naming convention in windows, I called `__pyx_f_13commonmodules_mainCode` after import module, and it worked :) Thank you very much David for the help. Really appreciate it – sdfdf Apr 23 '23 at 23:08
  • Hey @DavidW, would you know the answer to https://stackoverflow.com/questions/76088082/nameerror-name-ctypes-is-not-defined-cython-declaration-in-c ? I am facing another problem and am unable to find it in cython documentation – sdfdf Apr 25 '23 at 01:03