-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
I defined an iterable class in pybind11 by creating a __getitem__ method which throws a std::out_of_range exception if the index is out of range. While this successfully stops the iteration in python (without raising a python exception), it appears that stopping the iteration takes about 100us vs 1us for a native implementation, so ~100x slower. I tried the following things:
A. PyErr_SetString(PyExc_StopIteration, "..."); throw error_already_set();: takes ~25us, so ~4x faster than std::out_of_range but still ~25x slower than the native version.
B. PyErr_SetString(PyExc_StopIteration, "..."); and then propagating the error manually, without throwing a C++ exception. This takes ~5us, so ~20x faster than std::out_of_range and "only" ~5x slower than the native version.
However, option B requires returning a NULL PyObject to python, which the dispatcher doesn't like- it triggers the error here: https://github.com/pybind/pybind11/blob/master/include/pybind11/pybind11.h#L924-L928. In order to run option 2, I have to patch those lines to check PyErr_Occurred() and only set an error if none is already set.
so, my questions:
- Is there any equally or more efficient way to define an iterable object in C++ using pybind11 than
__getitem__and option B above? - If the answer to that is "no", can
cpp_function::dispatcherbe patched to allow setting a python error directly, without throwing a C++ exception?