Wrong first argument when using PyArg_ParseTuple

0

I write methods for a c extension type:

static PyObject *
RawGraphState_apply_C_L(RawGraphState * self
                        , PyObject * args)
{
    npy_uint8 vop = 0xdeadbeef;
    npy_intp i;// = 0xdeadbeef;

    if(!PyArg_ParseTuple(args, "II", &i, &vop))
    {
        return NULL;
    }

    printf("i = %ld\n", i);

    if(vop >= 24)
    {
        PyErr_SetString(PyExc_ValueError, "vop index must be in [0, 23]");
        return NULL;
    }

    if(i >= self->length)
    {
        PyErr_SetString(PyExc_ValueError, "qbit index out of range");
        return NULL;
    }

    printf("mapping vop[%ld] %d,%d -> %d\n", i, vop, self->vops[i], vop_lookup_table[vop][self->vops[i]]);
    self->vops[i] = vop_lookup_table[vop][self->vops[i]];

    Py_RETURN_NONE;

}

However this does not read the first argument correctly. It either defaults to 0 or to a huge number (depending on wheter I call the method in pytest or using ipython):

In [1]: from pyqcs.graph.backend.raw_state import RawGraphState                                                                        
^[[A
In [2]: g = RawGraphState(2)                                                                                                           

In [3]: g.apply_C_L(1, 0)                                                                                                              
i = 140200617443328
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-3-57081093fbb3> in <module>
----> 1 g.apply_C_L(1, 0)

ValueError: qbit index out of range

What am I doing wrong here?

--

You can find the code here:

https://github.com/daknuett/PyQCS/blob/graph_simulation/src/pyqcs/graph/backend/raw_state.c

starting at line 300.

python
python-3.x
python-c-api
asked on Stack Overflow Nov 6, 2019 by LittleByBlue

1 Answer

1

The format code I expects to be passed a pointer to an unsigned int. Not an npy_uint8 or an npy_intp. An unsigned int and only an unsigned int. An 8 bit uint like vop is is definitely the wrong size, as an attempt to write to it as if it were a normal unsigned int will likely overwrite some other data. A npy_intp might or might not be the right size, but since it could be wrong, don't use it.

For the first argument you want either b or B, which is an unsigned char (8 bits on pretty much every platform). Read the documentation for the difference. For the second argument would probably want to use K for an unsigned long long, pass it an unsigned long long (which should be big enough...), and then convert that to npy_intp checking for overflow yourself. Alternatively you might use O then call PyLong_AsVoidPtr on that to get the number to C.

As an aside: npy_uint8 vop = 0xdeadbeef; should probably generate a warning since the number won't fit into 8 bits.

answered on Stack Overflow Nov 6, 2019 by DavidW • edited Nov 6, 2019 by DavidW

User contributions licensed under CC BY-SA 3.0