I use cython to wrap C++ code and make it available in Python. The problem I'm facing is that I want to use a wrapped class as an argument in a function that I want to wrap as well. So from Python's point of view I want to create and modify an object of the wrapped class and use it as an argument for the wrapped function that I also want to call from Python. The code below will hopefully demonstrate this.
Below you can find a mininmal example in C++ which I want to wrap:
./cppCode/Settings/Settings.h
class Settings
{
public:
Settings();
void doSomething();
};
./cppCode/Helper/Helper.h
#include "../Settings/Settings.h"
void myFunction(Settings settings);
The functionality is not really important. Therefor I left out the .cpp files. Below is my approach in Cython so far:
./cythonCode/Settings/Settings.pxd
cdef extern from "../../cppCode/Settings/Settings.h":
cdef cppclass Settings:
Settings() except +
void doSomething()
./cythonCode/Settings/Settings.pyx
# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++
from Settings cimport Settings
cdef class PySettings:
cdef Settings c_settings
def __cinit__(self):
self.c_settings = Settings()
def doSomething(self):
self.c_settings.doSomething()
./cythonCode/Helper.pxd
from Settings.Settings cimport Settings
cdef extern from "../../cppCode/Helper/Helper.h":
void myFunction(Settings settings)
./cythonCode/Helper.pyx
# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++
from Helper cimport myFunction
cdef PyMyFunction(PySettings settings):
myFunction(settings)
run.py
import cythonCode.Settings.Settings as Settings
#import cythonCode.Helper as Helper
mySettings = Settings.PySettings()
mySettings.doSomething()
#Helper.myFunction(mySettings) # not working
I hope the structure of the project is clear. I would actually like to have the "Helper.pyx" and "Helper.pxd" in a folder "Helper" as well but then I don't know how to cimport Settings. If you can help me fix this it would also be highly appreciated. However, the main issue is to get Helper running at all such that I can use it in "run.py". The "setup.py" to build the cython modules simply looks like this:
from distutils.core import setup
from Cython.Build import cythonize
from setuptools.extension import Extension
extensions = [
Extension("Helper", ["Helper.pyx"])
]
setup(ext_modules=cythonize(extensions))
I do the same separately in the Settings folder.
I'd highly appreciate your help in solving this issue!
EDIT: As mentioned in the comments, there are two mistakes:
1) It should be Helper.PyMyFunction(mySettings) in run.py.
2) It should be def instead of cdef in front of PyMyFunction because I definitely want to call this function from Python.
EDIT2: I played around with your inputs and found an inelegant solution which leads to another question. So below is the code that works for me:
Settings.pxd
cdef extern from "../../cppCode/Settings/Settings.h":
cdef cppclass Settings:
Settings() except +
void doSomething()
cdef extern from "../../cppCode/Helper/Helper.h":
void myFunction(Settings settings)
Settings.pyx
# distutils: sources = [../../cppCode/Settings/Settings.cpp, ../../cppCode/Helper/Helper.cpp]
# distutils: language = c++
from Settings cimport Settings, myFunction
cdef class PySettings:
cdef Settings c_settings
def __cinit__(self):
self.c_settings = Settings()
def doSomething(self):
self.c_settings.doSomething()
def PyMyFunction(PySettings settings):
myFunction(settings.c_settings)
When I cythonize Settings.pyx I can run the following Python code and everything works fine:
import Settings
mySettings = Settings.PySettings()
mySettings.doSomething()
Settings.PyMyFunction(mySettings)
What I find inelegant about it is the fact that both parts (Settings and myFunction) are included in the same file. I have no idea how to get this running when these two parts are in separate files.
EDIT3: In order to tackle the "two parts are in separate files" issue imagine the following code:
Settings.pxd
cdef extern from "../../cppCode/Settings/Settings.h":
cdef cppclass Settings:
Settings() except +
void doSomething()
Settings.pyx
# distutils: sources = ../../cppCode/Settings/Settings.cpp
# distutils: language = c++
from Settings cimport Settings
cdef class PySettings:
cdef Settings c_settings
def __cinit__(self):
self.c_settings = Settings()
def doSomething(self):
self.c_settings.doSomething()
Helper.pxd
from Settings cimport Settings
cdef extern from "../../cppCode/Helper/Helper.h":
void myFunction(Settings settings)
Helper.pyx
# distutils: sources = ../../cppCode/Helper/Helper.cpp
# distutils: language = c++
from Helper cimport myFunction
def PyMyFunction(PySettings settings):
myFunction(settings.c_settings)
It is the same code as in EDIT2 but split up in two files. There is only one additional line in Helper.pxd which is "from Settings cimport Settings". However, in Helper.pyx I get this error:
def PyMyFunction(PySettings settings):
^
---------------------------------------
Helper.pyx:6:17: 'PySettings' is not a type identifier
I tried "from Settings cimport PySettings" but this doesn't work. The same error keeps occuring. All the files are in the same directory.