// Wrapper-helping odds and ends // // Copyright (C) 2009 Andreas Kloeckner // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, // copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following // conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES // OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. #ifndef PYCUDA_WRAP_HELPERS_HEADER_SEEN #define PYCUDA_WRAP_HELPERS_HEADER_SEEN #include <pybind11/pybind11.h> #include <pybind11/operators.h> namespace py = pybind11; #define PYTHON_ERROR(TYPE, REASON) \ { \ PyErr_SetString(PyExc_##TYPE, REASON); \ throw boost::python::error_already_set(); \ } #define ENUM_VALUE(NAME) \ value(#NAME, NAME) #define DEF_SIMPLE_METHOD(NAME) \ def(#NAME, &cls::NAME) #define DEF_SIMPLE_STATIC_METHOD(NAME) \ def_static(#NAME, &cls::NAME) #define DEF_SIMPLE_METHOD_WITH_ARGS(NAME, ARGS) \ def(#NAME, &cls::NAME, boost::python::args ARGS) #define DEF_SIMPLE_FUNCTION(NAME) \ m.def(#NAME, &NAME) #define DEF_SIMPLE_FUNCTION_WITH_ARGS(NAME, ARGS) \ m.def(#NAME, &NAME, py::args ARGS) #define DEF_SIMPLE_RO_MEMBER(NAME) \ def_readonly(#NAME, &cls::m_##NAME) #define DEF_SIMPLE_RW_MEMBER(NAME) \ def_readwrite(#NAME, &cls::m_##NAME) // }}} // {{{ COPY_PY_XXX #define COPY_PY_LIST(TYPE, NAME) \ { \ for (auto it: py_##NAME) \ NAME.push_back(py::cast<TYPE>(it)); \ } #define COPY_PY_ARRAY(FUNC_NAME, TYPE, NAME, COUNTER) \ { \ COUNTER = 0; \ for (auto it: py_##NAME) \ { \ if (COUNTER == NAME.size()) \ throw error(FUNC_NAME, \ CL_INVALID_VALUE, "too many entries in " #NAME " argument"); \ NAME[COUNTER++] = py::cast<TYPE>(it); \ } \ } #define COPY_PY_COORD_TRIPLE(NAME) \ size_t NAME[3] = {0, 0, 0}; \ { \ py::sequence py_seq_##NAME = py::cast<py::sequence>(py_##NAME); \ size_t my_len = len(py_seq_##NAME); \ if (my_len > 3) \ throw error("transfer", CL_INVALID_VALUE, #NAME "has too many components"); \ for (size_t i = 0; i < my_len; ++i) \ NAME[i] = py::cast<size_t>(py_seq_##NAME[i]); \ } #define COPY_PY_PITCH_TUPLE(NAME) \ size_t NAME[2] = {0, 0}; \ if (py_##NAME.ptr() != Py_None) \ { \ py::sequence py_seq_##NAME = py::cast<py::sequence>(py_##NAME); \ size_t my_len = len(py_seq_##NAME); \ if (my_len > 2) \ throw error("transfer", CL_INVALID_VALUE, #NAME "has too many components"); \ for (size_t i = 0; i < my_len; ++i) \ NAME[i] = py::cast<size_t>(py_seq_##NAME[i]); \ } #define COPY_PY_REGION_TRIPLE(NAME) \ size_t NAME[3] = {1, 1, 1}; \ { \ py::sequence py_seq_##NAME = py::cast<py::sequence>(py_##NAME); \ size_t my_len = len(py_seq_##NAME); \ if (my_len > 3) \ throw error("transfer", CL_INVALID_VALUE, #NAME "has too many components"); \ for (size_t i = 0; i < my_len; ++i) \ NAME[i] = py::cast<size_t>(py_seq_##NAME[i]); \ } // }}} #define PYOPENCL_PARSE_NUMPY_ARRAY_SPEC \ PyArray_Descr *tp_descr; \ if (PyArray_DescrConverter(dtype.ptr(), &tp_descr) != NPY_SUCCEED) \ throw py::error_already_set(); \ \ std::vector<npy_intp> shape; \ try \ { \ shape.push_back(py::cast<npy_intp>(py_shape)); \ } \ catch (py::cast_error &) \ { \ COPY_PY_LIST(npy_intp, shape); \ } \ \ NPY_ORDER order = PyArray_CORDER; \ PyArray_OrderConverter(py_order.ptr(), &order); \ \ int ary_flags = 0; \ if (order == PyArray_FORTRANORDER) \ ary_flags |= NPY_FARRAY; \ else if (order == PyArray_CORDER) \ ary_flags |= NPY_CARRAY; \ else \ throw std::runtime_error("unrecognized order specifier"); \ \ std::vector<npy_intp> strides; \ if (py_strides.ptr() != Py_None) \ { \ COPY_PY_LIST(npy_intp, strides); \ } #define PYOPENCL_RETURN_VECTOR(ITEMTYPE, NAME) \ { \ py::list pyopencl_result; \ for (ITEMTYPE item: NAME) \ pyopencl_result.append(item); \ return pyopencl_result; \ } namespace { template <typename T> inline py::object handle_from_new_ptr(T *ptr) { return py::cast(ptr, py::return_value_policy::take_ownership); } template <typename T, typename ClType> inline T *from_int_ptr(intptr_t obj_ref, bool retain) { ClType clobj = (ClType) obj_ref; return new T(clobj, retain); } template <typename T> inline intptr_t to_int_ptr(T const &obj) { return (intptr_t) obj.data(); } } #define PYOPENCL_EXPOSE_TO_FROM_INT_PTR(CL_TYPENAME) \ .def_static("from_int_ptr", from_int_ptr<cls, CL_TYPENAME>, \ py::arg("int_ptr_value"), \ py::arg("retain")=true, \ "(static method) Return a new Python object referencing the C-level " \ ":c:type:`" #CL_TYPENAME "` object at the location pointed to " \ "by *int_ptr_value*. The relevant ``clRetain*`` function " \ "will be called if *retain* is True." \ "If the previous owner of the object will *not* release the reference, " \ "*retain* should be set to *False*, to effectively transfer ownership to " \ ":mod:`pyopencl`." \ "\n\n.. versionadded:: 2013.2\n" \ "\n\n.. versionchanged:: 2016.1\n\n *retain* added.") \ .def_property_readonly("int_ptr", to_int_ptr<cls>, \ "Return an integer corresponding to the pointer value " \ "of the underlying :c:type:`" #CL_TYPENAME "`. " \ "Use :meth:`from_int_ptr` to turn back into a Python object." \ "\n\n.. versionadded:: 2013.2\n") \ #endif