aboutsummaryrefslogtreecommitdiff
path: root/libs/plugin_python/src/pybind11-utils/include/pybind11_utils/generator.h
blob: cbf9d186a738de23c5b6b6ab18cae8374c0c849c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#ifndef PYTHON_PYBIND11_GENERATOR_H
#define PYTHON_PYBIND11_GENERATOR_H

#include <generator>

#include <pybind11/pybind11.h>

namespace mo2::python {

    // the code here is mostly taken from pybind11 itself, and relies on some pybind11
    // internals so might be subject to change when upgrading pybind11 versions

    namespace detail {
        template <typename T>
        struct generator_state {
            std::generator<T> g;
            decltype(g.begin()) it;

            generator_state(std::generator<T> gen) : g(std::move(gen)), it(g.begin()) {}
        };
    }  // namespace detail

    // create a Python generator from a C++ generator
    //
    template <typename T, typename... Args>
    auto make_generator(std::generator<T> g, Args&&... args)
    {
        using state = detail::generator_state<T>;

        namespace py = pybind11;
        if (!py::detail::get_type_info(typeid(state), false)) {
            py::class_<state>(py::handle(), "iterator", pybind11::module_local())
                .def("__iter__",
                     [](state& s) -> state& {
                         return s;
                     })
                .def(
                    "__next__",
                    [](state& s) -> T {
                        if (s.it != s.g.end()) {
                            T v = *s.it;
                            s.it++;
                            return v;
                        }
                        else {
                            throw py::stop_iteration();
                        }
                    },
                    std::forward<Args>(args)...);
        }

        return py::cast(state{std::move(g)});
    }

}  // namespace mo2::python

#endif