Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't call PyInit function of a C++ Cython module from C #3250

Open
Keithcat1 opened this issue Nov 27, 2019 · 4 comments
Open

Can't call PyInit function of a C++ Cython module from C #3250

Keithcat1 opened this issue Nov 27, 2019 · 4 comments

Comments

@Keithcat1
Copy link

@Keithcat1 Keithcat1 commented Nov 27, 2019

Recently I am making a tool that allows you to build just one Python extension that contains multiple Cython modules, and I need to call the PyInit functions of embedded Cython modules.
My tool works with any modules that compile to C code, but if they compile to C++, I get a compiler error that the symbol PyInit for this module can't be found. It tells me about a similar symbol with, but I think it's mangled.
When I checked a C++ module, the PyInit function seemed to be declared as extern C.
Maybe the problem is that extern C only works if I were to include the Cython files directly, and not link to there .o file.
One idea, assuming Cython puts a list of the same functions at the top of each file, add an option to not add those functions and instead, to put those into an include file. Then in theory, I can include the Cython module, making the resulting extension noticeably smaller in the process.
Any help would be appreciated.
Also, thanks for Cython, I use it a lot and it's great which was why I wanted to make this tool.

@da-woods

This comment has been minimized.

Copy link
Contributor

@da-woods da-woods commented Dec 5, 2019

extern "C" prevents the name from being mangled. This is correct because when Python imports a compiled module it expects to find a function called PyInit_<filename>. If the name was mangled then it wouldn't find this function.

Making one or more functions public will cause Cython to generate a .h file that you can include.

I think a short example would be needed to demonstrate your problem: C++ Cython modules are tested pretty thoroughly so they do work!

@Keithcat1

This comment has been minimized.

Copy link
Author

@Keithcat1 Keithcat1 commented Dec 5, 2019

Of course they work.
But let's say I compile a C++ module, called mod.cpp and it has the init function called PyInit_mod
If I create another module, init_mod.c, and write:
cdef extern from : # * is required because including Cython files doesn't work and you need to link to the .o files instead.
"""
PyModuleDef
PyInit_mod();
"""
PyModuleDef* PyInit_mod()

cdef PyModuleDef* module=PyInit_mod()

This is how I load embedded extension modules, which seems to be the best way when embedding them in a single module.
This only works with C files though, and only by linking the .o or .obj files together.
Hope this made sense.

@da-woods

This comment has been minimized.

Copy link
Contributor

@da-woods da-woods commented Dec 6, 2019

This seems to works for me for both C and C++ modules. I wonder think your problem is that you're compiling init_mod with C++, and you are missing the extern "C".

If you make anything public then Cython will generate a header file which you can use instead of writing the header code in yourself. Just add a dummy public function:

cdef public void dummy():
     pass

I'll leave this conversation here since I don't think I have anything more to contribute.

@Keithcat1

This comment has been minimized.

Copy link
Author

@Keithcat1 Keithcat1 commented Dec 6, 2019

OK. My script is generating code to allow calling the PyInit function that looks exactly like this:
cdef extern from "t.h":
PyModuleDef* PyInit_t()

Then when compiling, I statically link to t.obj. If t was compiled as t.c, it works fine. If however the filename was t.cpp, I get a compiler error:
call.obj : error LNK2001: unresolved external symbol PyInit_t
Hint on symbols that are defined and could potentially match:
"struct _object * __cdecl PyInit_t(void)" (?PyInit_t@@YAPEAU_object@@xz)
C:\py\call.pyd : fatal error LNK1120: 1 unresolved externals

Are you saying I need to compile call to C++ code? Or is there an extern C marker Cython needs? Thanks a lot!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.