pybind11
pybind11 copied to clipboard
[QUESTION] How can I overload the [ ] operator?
I understand how the other operators are overloaded but I'm having difficulty with this one. This is my code (simplified):
float& Vec::operator[](unsigned i)
{
return privatedata[i];
}
py::class_<Vec>(m, "Vec")
.def(py::init<unsigned &>())
.def(unsigned() [] float());
It may not come as a surprise that this does not work. Since a Vec object is not passed in or out of the operator directly, my binding cannot contain py::self. Please let me know what I'm doing wrong! Thanks everyone.
found this, gonna try it: https://github.com/pybind/pybind11/issues/2702
EDIT: no luck, but my binding is now:
.def("__getitem__", (float (Vec::*)(unsigned)) &Vec::operator[]);
Does this work?
py::class_<Vec>(m, "Vec")
.def(py::init<unsigned &>())
.def("__setitem__", [](Vec* vec, unsigned index, float val) { (*vec)[index] = val; })
.def("__getitem__", &Vec::operator[]);
Thank you for the reply! I tried that, and it resulted in this compilation error:
src/bindings.cpp:158:10: error: no matching member function for call to 'def'
.def("__getitem__", &Vec::operator[]);
~^~~
It seemed to be fine with the setitem binding, though.
Can you show me the full code snippet?
Yes!
Binding:
py::class_<Vec>(m, "Vec")
.def(py::init<unsigned &>())
.def("size", &Vec::size)
.def("__setitem__", [](Vec* vec, unsigned index, float val) { (*vec)[index] = val; })
.def("__getitem__", &Vec::operator[]);
C++:
float Vec::operator[](unsigned i) const
{
GAPS_ASSERT(i < mSize);
return mData[i];
}
How I'm accessing it in Python (probably not relevant since I've never gotten it to compile, ha ha:
ax.plot(np.array(range(1, nsamples+1)), np.array(vector[i]), label="Data " + str(i))
Where vector is a Vec object. (Calls to size(), etc. work fine)
That's strange. Can you provide a minimal version of your source code files and push them somewhere so I can try to compile and see what's up?
The minimal version of the source code would be great, as @yasamoka mentioned.
However I assumed parts of your code:
#include "pybind11/pybind11.h"
#include <vector>
namespace py = pybind11;
class Vec
{
public:
Vec(unsigned int m_size) : mData(m_size, 0)
{
mData.reserve(m_size);
}
unsigned int size() const
{
return mData.size();
}
float* operator[](unsigned i)
{
return &mData[i];
}
private:
std::vector<float> mData;
};
PYBIND11_MODULE(mymodule, m)
{
py::class_<Vec>(m, "Vec")
.def(py::init<unsigned &>())
.def("size", &Vec::size)
.def("__setitem__", [](Vec &self, unsigned index, float val)
{ *self[index] = val; })
.def("__getitem__", [](Vec &self, unsigned index)
{ return self[index]; });
}
I tested it out for small array and assign some floats and print them. @jeanettejohnson is this working for you?
I like the proposed solution. However, you might want to be a little bit more protective to your python clients. In python when you return:
float* operator[](unsigned i)
{
return &mData[i];
}
a pointer in mData might be out of bounds - possibly corrupting data or crashing the python interpreter - when i > len(Vec). You would protect your clients better when you would change the above to:
float* operator[](unsigned i)
{
return &(mData.at(i));
}
Or do something similar in the PYBIND11_MODULE. The at() function will throw an out_of_range error, which will be caught by pybind11 en turned into a pythonic IndexError.
How would one extend this answer to deal with slicing as well? (Such that Vec[5:10] returns another vec with the relevant elements, for instance? I can pass an additional, but optional, end_idx into the function, for instance, but how would that work with a python slice object on python side?
Is there an equivalent to make a class behave like a dictionary?