/* pybind/cast.h: Partial template specializations to cast between C++ and Python types Copyright (c) 2015 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include #include #include NAMESPACE_BEGIN(pybind) NAMESPACE_BEGIN(detail) /// Generic type caster for objects stored on the heap template class type_caster { public: typedef instance instance_type; static std::string name() { return type_id(); } type_caster() { auto const& registered_types = get_internals().registered_types; auto it = registered_types.find(type_id()); if (it != registered_types.end()) typeinfo = &it->second; } bool load(PyObject *src, bool convert) { if (src == nullptr || typeinfo == nullptr) return false; if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) { value = ((instance_type *) src)->value; return true; } if (convert) { for (auto &converter : typeinfo->implicit_conversions) { temp = object(converter(src, typeinfo->type), false); if (load(temp.ptr(), false)) return true; } } return false; } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { if (policy == return_value_policy::automatic) policy = return_value_policy::copy; return cast(&src, policy, parent); } static PyObject *cast(const type *_src, return_value_policy policy, PyObject *parent) { type *src = const_cast(_src); if (src == nullptr) { Py_INCREF(Py_None); return Py_None; } // avoid an issue with internal references matching their parent's address bool dont_cache = policy == return_value_policy::reference_internal && parent && ((instance *) parent)->value == (void *) src; auto& internals = get_internals(); auto it_instance = internals.registered_instances.find(src); if (it_instance != internals.registered_instances.end() && !dont_cache) { PyObject *inst = it_instance->second; Py_INCREF(inst); return inst; } auto it = internals.registered_types.find(type_id()); if (it == internals.registered_types.end()) { std::string msg = std::string("Unregistered type : ") + type_id(); PyErr_SetString(PyExc_TypeError, msg.c_str()); return nullptr; } auto &type_info = it->second; instance_type *inst = (instance_type *) PyType_GenericAlloc(type_info.type, 0); inst->value = src; inst->owned = true; inst->parent = nullptr; if (policy == return_value_policy::automatic) policy = return_value_policy::take_ownership; handle_return_value_policy(inst, policy, parent); PyObject *inst_pyobj = (PyObject *) inst; type_info.init_holder(inst_pyobj); if (!dont_cache) internals.registered_instances[inst->value] = inst_pyobj; return inst_pyobj; } template ::value, int>::type = 0> static void handle_return_value_policy(instance *inst, return_value_policy policy, PyObject *parent) { if (policy == return_value_policy::copy) { inst->value = new T(*(inst->value)); } else if (policy == return_value_policy::reference) { inst->owned = false; } else if (policy == return_value_policy::reference_internal) { inst->owned = false; inst->parent = parent; Py_XINCREF(parent); } } template ::value, int>::type = 0> static void handle_return_value_policy(instance *inst, return_value_policy policy, PyObject *parent) { if (policy == return_value_policy::copy) { throw cast_error("return_value_policy = copy, but the object is non-copyable!"); } else if (policy == return_value_policy::reference) { inst->owned = false; } else if (policy == return_value_policy::reference_internal) { inst->owned = false; inst->parent = parent; Py_XINCREF(parent); } } operator type*() { return value; } operator type&() { return *value; } protected: type *value = nullptr; const type_info *typeinfo = nullptr; object temp; }; #define PYBIND_TYPE_CASTER(type, py_name) \ protected: \ type value; \ public: \ static std::string name() { return py_name; } \ static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \ return cast(*src, policy, parent); \ } \ operator type*() { return &value; } \ operator type&() { return value; } \ #define PYBIND_TYPE_CASTER_NUMBER(type, py_type, from_type, to_pytype) \ template <> class type_caster { \ public: \ bool load(PyObject *src, bool) { \ value = (type) from_type(src); \ if (value == (type) -1 && PyErr_Occurred()) { \ PyErr_Clear(); \ return false; \ } \ return true; \ } \ static PyObject *cast(type src, return_value_policy /* policy */, PyObject * /* parent */) { \ return to_pytype((py_type) src); \ } \ PYBIND_TYPE_CASTER(type, #type); \ }; PYBIND_TYPE_CASTER_NUMBER(int32_t, long, PyLong_AsLong, PyLong_FromLong) PYBIND_TYPE_CASTER_NUMBER(uint32_t, unsigned long, PyLong_AsUnsignedLong, PyLong_FromUnsignedLong) PYBIND_TYPE_CASTER_NUMBER(int64_t, PY_LONG_LONG, PyLong_AsLongLong, PyLong_FromLongLong) PYBIND_TYPE_CASTER_NUMBER(uint64_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong, PyLong_FromUnsignedLongLong) #if defined(__APPLE__) // size_t/ssize_t are separate types on Mac OS X PYBIND_TYPE_CASTER_NUMBER(ssize_t, Py_ssize_t, PyLong_AsSsize_t, PyLong_FromSsize_t) PYBIND_TYPE_CASTER_NUMBER(size_t, size_t, PyLong_AsSize_t, PyLong_FromSize_t) #endif PYBIND_TYPE_CASTER_NUMBER(float, float, PyFloat_AsDouble, PyFloat_FromDouble) PYBIND_TYPE_CASTER_NUMBER(double, double, PyFloat_AsDouble, PyFloat_FromDouble) template <> class type_caster { public: bool load(PyObject *, bool) { return true; } static PyObject *cast(void_type, return_value_policy /* policy */, PyObject * /* parent */) { Py_INCREF(Py_None); return Py_None; } PYBIND_TYPE_CASTER(void_type, "None"); }; template <> class type_caster { public: bool load(PyObject *src, bool) { if (src == Py_True) { value = true; return true; } else if (src == Py_False) { value = false; return true; } else return false; } static PyObject *cast(bool src, return_value_policy /* policy */, PyObject * /* parent */) { PyObject *result = src ? Py_True : Py_False; Py_INCREF(result); return result; } PYBIND_TYPE_CASTER(bool, "bool"); }; template <> class type_caster { public: bool load(PyObject *src, bool) { const char *ptr = PyUnicode_AsUTF8(src); if (!ptr) { PyErr_Clear(); return false; } value = std::string(ptr); return true; } static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) { return PyUnicode_FromString(src.c_str()); } PYBIND_TYPE_CASTER(std::string, "str"); }; #ifdef HAVE_WCHAR_H template <> class type_caster { public: bool load(PyObject *src, bool) { const wchar_t *ptr = PyUnicode_AsWideCharString(src, nullptr); if (!ptr) { PyErr_Clear(); return false; } value = std::wstring(ptr); return true; } static PyObject *cast(const std::wstring &src, return_value_policy /* policy */, PyObject * /* parent */) { return PyUnicode_FromWideChar(src.c_str(), src.length()); } PYBIND_TYPE_CASTER(std::wstring, "wstr"); }; #endif template <> class type_caster { public: bool load(PyObject *src, bool) { char *ptr = PyUnicode_AsUTF8(src); if (!ptr) { PyErr_Clear(); return false; } value = ptr; return true; } static PyObject *cast(const char *src, return_value_policy /* policy */, PyObject * /* parent */) { return PyUnicode_FromString(src); } static PyObject *cast(char src, return_value_policy /* policy */, PyObject * /* parent */) { char str[2] = { src, '\0' }; return PyUnicode_DecodeLatin1(str, 1, nullptr); } static std::string name() { return "str"; } operator char*() { return value; } operator char() { return *value; } protected: char *value; }; template class type_caster> { typedef std::pair type; public: bool load(PyObject *src, bool convert) { if (!PyTuple_Check(src) || PyTuple_Size(src) != 2) return false; if (!first.load(PyTuple_GetItem(src, 0), convert)) return false; return second.load(PyTuple_GetItem(src, 1), convert); } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { PyObject *o1 = type_caster::type>::cast(src.first, policy, parent); PyObject *o2 = type_caster::type>::cast(src.second, policy, parent); if (!o1 || !o2) { Py_XDECREF(o1); Py_XDECREF(o2); return nullptr; } PyObject *tuple = PyTuple_New(2); PyTuple_SetItem(tuple, 0, o1); PyTuple_SetItem(tuple, 1, o2); return tuple; } static std::string name() { return "(" + type_caster::name() + ", " + type_caster::name() + ")"; } operator type() { return type(first, second); } protected: type_caster::type> first; type_caster::type> second; }; template class type_caster> { typedef std::tuple type; public: enum { size = sizeof...(Tuple) }; bool load(PyObject *src, bool convert) { return load(src, convert, typename make_index_sequence::type()); } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { return cast(src, policy, parent, typename make_index_sequence::type()); } static std::string name(const char **keywords = nullptr, const char **values = nullptr) { std::array names {{ type_caster::type>::name()... }}; std::string result("("); int counter = 0; for (auto const &name : names) { if (keywords && keywords[counter]) { result += keywords[counter]; result += " : "; } result += name; if (values && values[counter]) { result += " = "; result += values[counter]; } if (++counter < size) result += ", "; } result += ")"; return result; } template typename std::enable_if::value, ReturnValue>::type call(Func &&f) { return call(std::forward(f), typename make_index_sequence::type()); } template typename std::enable_if::value, void_type>::type call(Func &&f) { call(std::forward(f), typename make_index_sequence::type()); return void_type(); } operator type() { return cast(typename make_index_sequence::type()); } protected: template ReturnValue call(Func &&f, index_sequence) { return f((Tuple) std::get(value)...); } template type cast(index_sequence) { return type((Tuple) std::get(value)...); } template bool load(PyObject *src, bool convert, index_sequence) { if (!PyTuple_Check(src)) return false; if (PyTuple_Size(src) != size) return false; std::array results {{ (PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get(value).load(PyTuple_GET_ITEM(src, Indices), convert) : false)... }}; (void) convert; /* avoid a warning when the tuple is empty */ for (bool r : results) if (!r) return false; return true; } /* Implementation: Convert a C++ tuple into a Python tuple */ template static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent, index_sequence) { std::array results {{ type_caster::type>::cast(std::get(src), policy, parent)... }}; bool success = true; for (auto result : results) if (result == nullptr) success = false; if (success) { PyObject *tuple = PyTuple_New(size); int counter = 0; for (auto result : results) PyTuple_SetItem(tuple, counter++, result); return tuple; } else { for (auto result : results) { Py_XDECREF(result); } return nullptr; } } protected: std::tuple::type>...> value; }; /// Type caster for holder types like std::shared_ptr, etc. template class type_caster_holder : public type_caster { public: typedef type_caster parent; bool load(PyObject *src, bool convert) { if (!parent::load(src, convert)) return false; holder = holder_type(parent::value); return true; } explicit operator type*() { return this->value; } explicit operator type&() { return *(this->value); } explicit operator holder_type&() { return holder; } explicit operator holder_type*() { return &holder; } protected: holder_type holder; }; template <> class type_caster { public: bool load(PyObject *src) { value = handle(src); return true; } static PyObject *cast(const handle &src, return_value_policy /* policy */, PyObject * /* parent */) { src.inc_ref(); return (PyObject *) src.ptr(); } PYBIND_TYPE_CASTER(handle, "handle"); }; #define PYBIND_TYPE_CASTER_PYTYPE(name) \ template <> class type_caster { \ public: \ bool load(PyObject *src, bool) { value = name(src, true); return true; } \ static PyObject *cast(const name &src, return_value_policy /* policy */, PyObject * /* parent */) { \ src.inc_ref(); return (PyObject *) src.ptr(); \ } \ PYBIND_TYPE_CASTER(name, #name); \ }; PYBIND_TYPE_CASTER_PYTYPE(object) PYBIND_TYPE_CASTER_PYTYPE(buffer) PYBIND_TYPE_CASTER_PYTYPE(capsule) PYBIND_TYPE_CASTER_PYTYPE(dict) PYBIND_TYPE_CASTER_PYTYPE(float_) PYBIND_TYPE_CASTER_PYTYPE(int_) PYBIND_TYPE_CASTER_PYTYPE(list) PYBIND_TYPE_CASTER_PYTYPE(slice) PYBIND_TYPE_CASTER_PYTYPE(tuple) PYBIND_TYPE_CASTER_PYTYPE(function) NAMESPACE_END(detail) template inline T cast(PyObject *object) { detail::type_caster::type> conv; if (!conv.load(object, true)) throw cast_error("Unable to cast Python object to C++ type"); return conv; } template inline object cast(const T &value, return_value_policy policy = return_value_policy::automatic, PyObject *parent = nullptr) { if (policy == return_value_policy::automatic) policy = std::is_pointer::value ? return_value_policy::take_ownership : return_value_policy::copy; return object(detail::type_caster::type>::cast(value, policy, parent), false); } template inline T handle::cast() { return pybind::cast(m_ptr); } template inline object handle::call(Args&&... args_) { const size_t size = sizeof...(Args); std::array args{ { detail::type_caster::type>::cast( std::forward(args_), return_value_policy::automatic, nullptr)... } }; bool fail = false; for (auto result : args) if (result == nullptr) fail = true; if (fail) { for (auto result : args) { Py_XDECREF(result); } throw cast_error("handle::call(): unable to convert input arguments to Python objects"); } PyObject *tuple = PyTuple_New(size); int counter = 0; for (auto result : args) PyTuple_SetItem(tuple, counter++, result); PyObject *result = PyObject_CallObject(m_ptr, tuple); Py_DECREF(tuple); return object(result, false); } NAMESPACE_END(pybind)