2

I am implementing a Visual C++ code (MS VStudio 2013) that acquires an image from a camera, processes the image, sends pre-processed data (an array 2 or more doubles) to python through a PyObject, and gets numpy-processed results back from python (a tuple of 3 doubles in py, which is becomes an array of 3 doubles in C++). I tested the py function as a standalone script. However, I keep getting a null as a return from PyObject_CallObject(). python and the VC++ project reside in the same directory.

I have set up a log file record in the py function to document the calls from C, so I know that in some cases the C++ calls do not reach the py function, and in other cases the py function does get called, but, perhaps, the returned values cannot be processed. Specifically, I found that I can call the py function (positive log record) when the tuple contains one object, but I cannot call it (no log file records) when the tuple contains two or more objects. In addition, if the py function needs no arguments, and does not need to return anything, all calls are successful.

I have googled and read everything having to do with using Tuples to communicate between C++ and py, but still did not make it work. I have also read the documentation and the examples, still no luck.

I have stripped the code to a bare minimum below and reproduced the problem. Below is an example when I pass two zeros as input, and expect to get three zeros back. I would be very grateful for any leads on this...

//C++ part
    Py_Initialize();    
    pyName = PyImport_ImportModuleNoBlock(_T("find_center_def"));
    if (pyName == NULL) {       
        return false;
    }
    pyFunc = PyObject_GetAttrString(pyName, _T("find_center"));
    if (pyFunc == NULL) {
        return false;
    }
    Py_DECREF(pyName);
    if (PyCallable_Check(pyFunc) == 0) {
        return false;
    }
    PyObject * arg = PyTuple_New(2);
    PyObject * datum0 = PyFloat_FromDouble(0.0);
    PyObject * datum1 = PyFloat_FromDouble(0.0);
    if (PyTuple_SetItem(arg, 0, datum0) != 0) {
        return false;
        }
    if (PyTuple_SetItem(arg, 1, datum1) != 0) {         
        return false;
        }
    PyObject * result = PyObject_CallObject(pyFunc, arg);
    if (result == NULL) {
        return false;
    }
    OutputDebugString(_T("\nSuccess calling find_center function!"));

#python part, inside of a script called find_center_def.py
def find_center(args):
    import numpy        
    fo=open('foo.txt' ,'w')
    fo.write('find_center was called from C++!')
    fo.close()
    return (0,0,0)

#python output:
Python 3.4.3 (v3.4.3:9b73f1c3e601, Feb 24 2015, 22:44:40) [MSC v.1600 64 bit (AMD64)] on win32
>>> import importlib
>>> test = importlib.import_module("find_center_def")
>>> test.find_center((0,0))
(0, 0, 0)
>>>
  • 1
    Do either `PyObject_GetAttrString(pyName, _T("find_center"))` or `PyTuple_SetItem(arg, 1, datum1)` return 0? – Adam Apr 22 '15 at 17:23
  • Adam -- they all return 0 (i.e. are successful), the code runs all the way to the final NULL== result. – runcyclexcski Apr 22 '15 at 17:42
  • don't know if it's a clue, but if I make the py function return any non-zero number (say 15), C++ replaces it with a zero: \nPyObject * result = PyObject_CallObject(pyFunc, arg);int final_result = PyLong_AsLong(result); (final result is always 0, or 0.0 if I use doubles) – runcyclexcski Apr 22 '15 at 17:50
  • 1
    I apologize for responding to myself. I found that if I simplify making of the PyTuple argument by calling PyObject * arg = Py_BuildValue("(d)", 5.0);, the symptoms remain -- the call succeeds when the tuple length is 1 (one d in the format string), but fails when tuple length is >1 (two ore more ds in the format string). Responses get zeroed out though, not sure if it's a part of the same problem. – runcyclexcski Apr 22 '15 at 18:05
  • Does `PyCallable_Check(pyFunc)` return 1? – Adam Apr 22 '15 at 20:06
  • 1
    Adam -- yes, the function is callable, and all sanity checks are OK up to the last call. I have *partially* worked around the issue by setting the argument of PyObject_CallObject to a tuple of size 1, containing one list of doubles of size N. I have never managed to call PyObject_CallObject using a tuple of size>1 as argument. I am now getting a response back, packed in a tuple of three doubles (which so far only gives me 0.0 upon parsing -- may be it's a unicode issue) – runcyclexcski Apr 22 '15 at 20:33

1 Answers1

3

You're only able to add a single value to your tuple because

PyObject* PyObject_CallObject(PyObject *callable_object, PyObject *args) Call a callable Python object callable_object, with arguments given by the tuple args

(emphasis mine). In other words, the second argument to PyObject_CallObject is a tuple of the arguments. Your function takes a single argument, so the length of that tuple has to be 1.

Since you're trying to pass a tuple as that one argument, you need your two-element tuple to be the first element of a single-element tuple that you pass as the second argument to PyObject_CallObject.

Another option is to use PyObject_CallFunction instead, and let it build the argument tuple.

Adam
  • 16,808
  • 7
  • 52
  • 98
  • Adam, yes, I got it experimentally, and I understand it now. May be the documentation could have stated it more explicitly that the length of the tuple should be 1. It was not obvious from examples I have found, which used PyTuple_SetItem, because the latter used item indices >0. At any rate, using *PyObject * arg = Py_BuildValue("(O)", list)* to make my input tuple, where *list* was build by PyList_SetItem, worked. And *list* could also have been the N-element tuple, as you said. – runcyclexcski Apr 22 '15 at 21:28
  • One more thing... the return format from python was proper, but I was getting zeros, because my VS2013 defaulted to Unicode and I was using a non-Unicode method to report values. – runcyclexcski Apr 22 '15 at 21:31