I have some C++ code, some python code and some cython code. In C++, I have an asynchronous callback that gets executed, and I want python code to be executed.
Here's what I did. In python, I wrote a function that took 2 parameters:
def fn(x,y):
print("hello")
print(x)
print(y)
Then, in C++, I wanted to call this function "fn" asynchronously as a callback. So I created a C++ function called "PythonCallback" to wrap the callback.
class PythonCallback{
public:
PyObject *_pyfunc;
PythonCallback(PyObject *pyfunc);
void presentCallback(_bstr_t EventName, BYTE* CallbackData);
void addCallbackToGUID( PyObject* Py_GUID );
}
//Constructor
PythonCallback::PythonCallback(PyObject *pyfunc){
if( PyCallable_Check(pyfunc)){
Py_INCREF(pyfunc);
_pyfunc = pyfunc;
}else{
throw -1;
}
};
void PythonCallback::presentCallback(_bstr_t EventName, BYTE* pCallbackData)
{
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
EventData* evData = (EventData*)pCallbackData;
PyObject *args = Py_BuildValue("(ss)", EventName, EventName);
const wchar_t* wstring(EventName);
PyObject_CallObject(_pyfunc, args );
std::wstring w;
w.append(EventName);
// PyObject_CallFunction( _pyfunc, "(ss)", wstring, wstring);
// PyObject_CallFunction( _pyfunc, "(ss)", w.c_str(),w.c_str());
// PyObject_CallFunction( _pyfunc, "(s)", w.c_str() );
// PyObject_CallFunction( _pyfunc, "" );
// PyObject_CallFunction( _pyfunc, "ss", wstring, wstring );
PyGILState_Release(gstate);
};
And all of this is glued together with Cython. In Cython, I created a class that would send the python function to C++
cdef class CallbackInformation:
cdef PythonCallback *thisptr
def __cinit__(self, object func):
self.thisptr = new PythonCallback(func)
# temp = PythonCallback(func)
def __dealloc__(self):
del self.thisptr
def addCallbackToGUID(self,guid):
self.thisptr.addCallbackToGUID(guid)
So, then what I would do is create a new instance of CallbackInformation
and pass it the fn
. i.e. instance = CallbackInformation(fn)
The problem happens when I call the python callback function fn
.
I don't get an immediate error, but when I try to just check fn
in the python console, i.e. if I just type fn in the console, I get the following error:
File "C:/Users/eric/PycharmProjects/SuperResolution/Startup3dCalibration.py", >line 62, in fn print("hello") UnicodeDecodeError: 'utf-8' codec can't decode byte 0xf8 in position 0: invalid >start byte
if I do it again, I get his message
hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello
and finally, if I do it a third time, I get the expected output:
<function fn at 0x0000000002281048>
Where did I go wrong?
Yet again, the solution comes right after you post the question:
so I realized that with Py_BuildValue
, there's a fundamental difference between a char *
string and a unicode string. So, simply replacing PyObject *args = Py_BuildValue("(ss)", EventName, EventName);
with PyObject *args = Py_BuildValue("(uu)", EventName, EventName);
fixed my problem.
I guess the error about unconvertable unicode makes sense in the end.