12 #include <hipipe/build_config.hpp>
13 #ifdef HIPIPE_BUILD_PYTHON
15 #include <hipipe/core/python/utility/pyboost_is_registered.hpp>
17 #include <boost/python.hpp>
18 #include <range/v3/core.hpp>
26 namespace hipipe::python {
30 struct stop_iteration_exception :
public std::runtime_error {
31 stop_iteration_exception()
32 : std::runtime_error{
"stop iteration"}
37 inline void stop_iteration_translator(
const stop_iteration_exception& x)
39 PyErr_SetNone(PyExc_StopIteration);
60 template<
typename Rng>
63 std::shared_ptr<Rng> rng_ptr_;
66 CPP_template(
int dummy = 0)(requires ranges::sized_range<const Rng>)
67 static void register_len(boost::python::class_<range<Rng>>& cls)
69 cls.def(
"__len__", &range<Rng>::len<>);
71 CPP_template(
int dummy = 0)(requires !ranges::sized_range<const Rng>)
72 static void register_len(boost::python::class_<range<Rng>>&)
77 CPP_template(
int dummy = 0)(requires ranges::random_access_range<const Rng>)
78 static void register_getitem(boost::python::class_<range<Rng>>& cls)
80 cls.def(
"__getitem__", &range<Rng>::getitem<>);
82 CPP_template(
int dummy = 0)(requires !ranges::random_access_range<const Rng>)
83 static void register_getitem(boost::python::class_<range<Rng>>&)
89 static void register_to_python()
91 namespace py = boost::python;
93 if (!utility::is_registered<range<Rng>>()) {
94 std::string this_name = std::string(
"hipipe_") +
typeid(range<Rng>).name();
95 py::class_<range<Rng>> cls{this_name.c_str(), py::no_init};
96 cls.def(
"__iter__", &range<Rng>::iter);
98 register_getitem(cls);
100 py::class_<range<Rng>::iterator>{(this_name +
"_iterator").c_str(), py::no_init}
101 .def(
"__next__", &range<Rng>::iterator::next)
102 .def(
"__iter__", &range<Rng>::iterator::iter);
109 std::shared_ptr<Rng> rng_ptr_;
110 ranges::iterator_t<Rng> position_;
111 bool first_iteration_;
114 iterator() =
default;
117 explicit iterator(range& rng)
118 : rng_ptr_{rng.rng_ptr_},
119 position_{ranges::begin(*rng_ptr_)},
120 first_iteration_{
true}
136 if (!first_iteration_ && position_ != ranges::end(*rng_ptr_)) ++position_;
137 first_iteration_ =
false;
138 if (position_ == ranges::end(*rng_ptr_))
throw stop_iteration_exception();
147 explicit range(Rng rng)
148 : rng_ptr_{std::make_shared<Rng>(std::move(rng))}
150 register_to_python();
154 range(std::shared_ptr<Rng> rng_ptr)
155 : rng_ptr_{std::move(rng_ptr)}
157 register_to_python();
163 return iterator{*
this};
169 CPP_template(
int dummy = 0)(requires ranges::sized_range<const Rng>)
170 boost::python::object getitem(PyObject* idx_py)
const
173 if (PySlice_Check(idx_py)) {
174 PySliceObject* slice = static_cast<PySliceObject*>(static_cast<void*>(idx_py));
175 if (slice->step != Py_None) {
176 throw std::logic_error(
"HiPipe python range does not support slice steps.");
179 auto handle_index = [
this](PyObject* idx_py,
long def_val) {
181 if (idx_py != Py_None) {
182 idx = boost::python::extract<long>(idx_py);
184 if (idx < 0) idx += this->len();
186 if (idx < 0) idx = 0;
188 if (idx > this->len()) idx = len();
192 long start = handle_index(slice->start, 0);
193 long stop = handle_index(slice->stop, len());
194 if (start > stop) start = stop;
196 using slice_data_type = std::vector<ranges::range_value_t<Rng>>;
197 slice_data_type slice_data{rng_ptr_->begin() + start, rng_ptr_->begin() + stop};
198 return boost::python::object{range<slice_data_type>{std::move(slice_data)}};
202 long idx = boost::python::extract<long>(idx_py);
203 if (idx < 0) idx += len();
204 return boost::python::object{ranges::at(*rng_ptr_, idx)};
208 CPP_template(
int dummy = 0)(requires ranges::sized_range<const Rng>)
211 return ranges::size(*rng_ptr_);
218 #endif // HIPIPE_BUILD_PYTHON