Search code examples
c++python-3.xpython-c-api

How can I query the data of a complex return type using PyObject_CallObject?


I have a small python programm that computes a 2d positon

def getPosition(lerpParameter):
    A = getClothoidConstant()
    ...

The python script can be found in my project folder here.

The function getPosition is included in the file clothoid.py. The function returns a value of type Vector2. How can I access the data of the returned Vector2d in my C++ program?

The C++ programm looks like this:

    /// Get function
    PyObject  *pFunc = PyObject_GetAttrString(pModule, pid.functionName);
    // pFunc is a new reference

    if (pFunc && PyCallable_Check(pFunc)) 
    {
        PyObject *pArgs = PyTuple_New(pid.getArgumentCount());

        PyObject *pValue = nullptr;

        // setup function parameters
        for (int i = 0; i < pid.getArgumentCount(); ++i) 
        {
            if (pid.arguments[i].type == eType::Int)
            {
                pValue = PyLong_FromLong(atoi(pid.arguments[i].name.c_str()));
            }
            else
            {
                pValue = PyFloat_FromDouble(atof(pid.arguments[i].name.c_str()));
            }

            if (!pValue) 
            {
                Py_DECREF(pArgs);
                Py_DECREF(pModule);
                std::cout << "Cannot convert argument" << std::endl;
                throw std::runtime_error("Cannot convert argument");
            }
            // pValue reference stolen here:
            PyTuple_SetItem(pArgs, i, pValue);
        }

        pValue = PyObject_CallObject(pFunc, pArgs);
        Py_DECREF(pArgs);

        if (pValue != nullptr)
        {
            switch (pid.returnType)
            {
...

            case eType::Double:
                //std::cout << "Result of call: " << PyFloat_AsDouble(pValue) << std::endl;
                doubleValue_ = PyFloat_AsDouble(pValue);
                break;

            case  eType::Vector2d:
                {
                    // How can I acccess the data here?

                }
                break;

            default:
                break;
            }

            Py_DECREF(pValue);
        }

If the return type is a double or int it is easy to get the corresponding value. But I have no idea how to access the data of the Vector2.


Solution

  • If you want to get the 'x' attribute, and you know it's a float:

    PyObject *temp = PyObject_GetAttrString(pValue, "x");
    if (temp == NULL) {
        // error handling
    }
    double x = PyFloat_AsDouble(temp);
    
    // clean up reference when done with temp
    Py_DECREF(temp);