From cd687d2ac40f92370693e6d1e8698e637eec8a30 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Mon, 17 Jun 2024 16:53:23 -0400 Subject: [PATCH] xo-pyutil: + pycaller assistant for registering function pointers --- include/xo/pyutil/pycaller.hpp | 132 +++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 include/xo/pyutil/pycaller.hpp diff --git a/include/xo/pyutil/pycaller.hpp b/include/xo/pyutil/pycaller.hpp new file mode 100644 index 0000000..85bf665 --- /dev/null +++ b/include/xo/pyutil/pycaller.hpp @@ -0,0 +1,132 @@ +/** @file pycaller.hpp + * + * Author: Roland Conybeare + **/ + +#pragma once + +#include + +//#include + +namespace xo { + namespace pyutil { + struct pycaller_base { + virtual ~pycaller_base() = default; +g + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pybind11::class_(m, "pycaller_base"); + } + return m; + } + }; + + /** Invoke function pointer of type Retval(*)(Args...), + * with arguments converted from py::object, and return type converted back to py::object. + * + * Each distinct combination of {Retval,Args...} needs to be established at compile time + * (since we need PyCall to be instantiated for particular types) + * + * Use when we don't know function pointer until *runtime*, + * for example getting function pointer from just-compiled code using xo-pyjit + **/ + template + struct pycaller; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller0") + .def("__call__", + [](self_type & self) + { + return pybind11::cast((*self.fptr_)()); + }); + } + return m; + } + + pybind11::object operator()() { return pybind11::cast((*fptr_)()); } + + private: + function_type fptr_; + }; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(Arg1); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller1") + .def("__call__", + [](self_type & self, Arg1 arg1) + { + return pybind11::cast((*self.fptr_)(arg1)); + }) + ; + } + return m; + } + + pybind11::object operator()(pybind11::object arg1) { + return pybind11::cast((*fptr_)(pybind11::cast(arg1))); + } + + private: + function_type fptr_; + }; + + template + struct pycaller : public pycaller_base { + using self_type = pycaller; + using function_type = Retval (*)(Arg1, Arg2); + + pycaller(function_type addr) : fptr_{reinterpret_cast(addr)} {} + + static pybind11::module & declare_once(pybind11::module & m) { + static bool s_once = false; + if (!s_once) { + s_once = true; + pycaller_base::declare_once(m); + pybind11::class_(m, "pycaller2") + .def("__call__", + [](self_type & self, Arg1 arg1, Arg2 arg2) + { + return pybind11::cast((*self.fptr_)(arg1, arg2)); + }) + ; + } + return m; + } + + pybind11::object operator()(pybind11::object arg1, pybind11::object arg2) { + return pybind11::cast((*fptr_)(pybind11::cast(arg1), + pybind11::cast(arg2))); + } + + private: + function_type fptr_; + }; + } /*namespace pyutil*/ +} + +/** end pycaller.hpp **/