Chiel
Chiel

Reputation: 6194

Overloaded operator() is failing in Cython

I am trying to wrap a handwritten templated C++ array class in Python using Cython. The code does not compile, because the operator() that should return a reference triggers an error during compilation: Cannot assign to or delete this. How can I make my code compile?

My .pyx file is:

# distutils: language = c++
import cython
import numpy as np
cimport numpy as np

cdef extern from "<array>" namespace "std":
    cdef cppclass Dim_array_2 "std::array<int, 2>":
        Dim_array_2() except+
        int& operator[](size_t)

cdef extern from "Array.h":
    cdef cppclass Array_2 "Array<double>":
        Array_2() except +
        void set_dims(const Dim_array_2&) except +
        double& operator()(const Dim_array_2&)

def print_index():
    cdef Dim_array_2 dims
    dims[0] = 3
    dims[1] = 5
    cdef Array_2 a
    a.set_dims(dims)

    cdef Dim_array_2 index

    counter = 0.
    for j in range(dims[1]):
        for i in range(dims[0]):
            index[0] = i+1
            index[1] = j+1
            a(index) = counter # This fails.
            counter += 1.

    index[0] = 2
    index[1] = 3
    print(a(index))

A simplified version of the corresponding C++ Array.h is:

#include <array>
#include <vector>

template<typename TF>
class Array
{
    public:
        Array() {}
        void set_dims(const std::array<int,2>& dims)
        {
            data.resize(dims[0]*dims[1]);
            this->dims = dims;
        }

        TF& data operator()(const std::array<int, 2>& index)
        {
            return data[ (index[0]-1) + (index[1]-1)*dims[0] ];
        }

    private:
        std::vector<TF> data;
        std::array<int,2> dims;
};

The complete error message is:

Error compiling Cython file:
------------------------------------------------------------
...
    counter = 0.
    for j in range(dims[1]):
        for i in range(dims[0]):
            index[0] = i+1
            index[1] = j+1
            a(index) = counter # This fails.
            ^
------------------------------------------------------------

test.pyx:31:13: Cannot assign to or delete this

Upvotes: 1

Views: 232

Answers (1)

DavidW
DavidW

Reputation: 30912

I think operator[] has been specifically special-cased to work, so the fact it works in supposedly similar circumstances doesn't tell you much.

I think in general Cython will struggle to make this syntax work. It doesn't completely understand C++ references, and C++ calls often use temporary variables (to help wrap the C++ exception handling), and these definitely don't work well with references.

You'll probably have to abandon the "nice" array(index) = value syntax, and write a small C++ wrapper function to help you:

cdef extern from "Array.h":
    """
    void setArrayValue(Array<double>& arr, const std::array<int, 2>& idx, double value) {
        arr(idx) = value;
    }
    """
    # ...

(I've used Cython's docstring syntax for including small C/C++ code snippets.)

Within your Cython code you'd then do setArrayValue(a, index, counter) instead.

Upvotes: 2

Related Questions