diff --git a/README.md b/README.md index 6167fe000..b73947585 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,13 @@ C++11-compatible compilers are widely available, this heavy machinery has become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python with -everything stripped away that isn't relevant for binding generation. The core -header files only require ~2.5K lines of code and depend on Python (2.7 or 3.x) -and the C++ standard library. This compact implementation was possible thanks -to some of the new C++11 language features (specifically: tuples, lambda -functions and variadic templates). Since its creation, this library has grown -beyond Boost.Python in many ways, leading to dramatically simpler binding code -in many common situations. +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~2.5K lines of code and depend on +Python (2.7 or 3.x) and the C++ standard library. This compact implementation +was possible thanks to some of the new C++11 language features (specifically: +tuples, lambda functions and variadic templates). Since its creation, this +library has grown beyond Boost.Python in many ways, leading to dramatically +simpler binding code in many common situations. Tutorial and reference documentation is provided at [http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest). diff --git a/docs/intro.rst b/docs/intro.rst index cb9dc9c5b..6eb5038fd 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -19,13 +19,13 @@ C++11-compatible compilers are widely available, this heavy machinery has become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python with -everything stripped away that isn't relevant for binding generation. The core -header files only require ~2.5K lines of code and depend on Python (2.7 or 3.x) -and the C++ standard library. This compact implementation was possible thanks -to some of the new C++11 language features (specifically: tuples, lambda -functions and variadic templates). Since its creation, this library has grown -beyond Boost.Python in many ways, leading to dramatically simpler binding code -in many common situations. +everything stripped away that isn't relevant for binding generation. Without +comments, the core header files only require ~2.5K lines of code and depend on +Python (2.7 or 3.x) and the C++ standard library. This compact implementation +was possible thanks to some of the new C++11 language features (specifically: +tuples, lambda functions and variadic templates). Since its creation, this +library has grown beyond Boost.Python in many ways, leading to dramatically +simpler binding code in many common situations. Core features ************* diff --git a/example/example4.ref b/example/example4.ref index 9be2bdc76..040d55b85 100644 --- a/example/example4.ref +++ b/example/example4.ref @@ -10,7 +10,7 @@ test_function(enum=1) None test_function(enum=2) None - + EMode.EFirstMode EMode.EFirstMode Example4::test_function(enum=1) diff --git a/example/run_test.py b/example/run_test.py index 8977e9672..802b851b5 100755 --- a/example/run_test.py +++ b/example/run_test.py @@ -22,6 +22,8 @@ def sanitize(lines): line = line.replace('__builtin__', 'builtins') line = line.replace('example.', '') line = line.replace('unicode', 'str') + line = line.replace('Example4.EMode', 'EMode') + line = line.replace('example.EMode', 'EMode') line = line.replace('method of builtins.PyCapsule instance', '') line = line.strip() if sys.platform == 'win32': diff --git a/include/pybind11/common.h b/include/pybind11/common.h index e294cfa7b..334ec137c 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -225,8 +225,8 @@ struct argument_entry { /// Internal data struture used to track registered instances and types struct internals { std::unordered_map registered_types_cpp; // std::type_info* -> type_info - std::unordered_map registered_types_py; // PyTypeObject* -> type_info - std::unordered_map registered_instances; // void * -> PyObject* + std::unordered_map registered_types_py; // PyTypeObject* -> type_info + std::unordered_map registered_instances; // void * -> PyObject* std::unordered_set, overload_hash> inactive_overload_cache; }; @@ -271,4 +271,7 @@ struct error_already_set : public std::runtime_error { public: error_already_set /// Thrown when pybind11::cast or handle::call fail due to a type casting error struct cast_error : public std::runtime_error { public: cast_error(const std::string &w = "") : std::runtime_error(w) {} }; +PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); } +PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); } + NAMESPACE_END(pybind11) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c35f837c4..9749147f8 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -24,7 +24,6 @@ #endif #include "cast.h" -#include NAMESPACE_BEGIN(pybind11) @@ -196,9 +195,9 @@ protected: a.value, return_value_policy::automatic, nullptr); if (obj == nullptr) - throw std::runtime_error("arg(): could not convert default keyword " - "argument into a Python object (type not " - "registered yet?)"); + pybind11_fail("arg(): could not convert default keyword " + "argument into a Python object (type not " + "registered yet?)"); entry->args.emplace_back(a.name, a.descr, obj); } @@ -490,7 +489,7 @@ protected: } else if (c == '%') { const std::type_info *t = types[type_index++]; if (!t) - throw std::runtime_error("Internal error while parsing type signature (1)"); + pybind11_fail("Internal error while parsing type signature (1)"); auto it = registered_types.find(t); if (it != registered_types.end()) { signature += ((const detail::type_info *) it->second)->type->tp_name; @@ -504,7 +503,7 @@ protected: } } if (type_depth != 0 || types[type_index] != nullptr) - throw std::runtime_error("Internal error while parsing type signature (2)"); + pybind11_fail("Internal error while parsing type signature (2)"); #if !defined(PYBIND11_CPP14) delete[] types; @@ -519,7 +518,7 @@ protected: #endif if (!m_entry->args.empty() && (int) m_entry->args.size() != args) - throw std::runtime_error( + pybind11_fail( "cpp_function(): function \"" + std::string(m_entry->name) + "\" takes " + std::to_string(args) + " arguments, but " + std::to_string(m_entry->args.size()) + " pybind11::arg entries were specified!"); @@ -555,7 +554,7 @@ protected: }); m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr()); if (!m_ptr) - throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object"); + pybind11_fail("cpp_function::cpp_function(): Could not allocate function object"); } else { /* Append at the end of the overload chain */ m_ptr = m_entry->sibling; @@ -597,7 +596,7 @@ protected: m_ptr = PyMethod_New(m_ptr, nullptr, entry->class_); #endif if (!m_ptr) - throw std::runtime_error("cpp_function::cpp_function(): Could not allocate instance method object"); + pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); Py_DECREF(func); } } @@ -621,7 +620,7 @@ public: m_ptr = Py_InitModule3(name, nullptr, doc); #endif if (m_ptr == nullptr) - throw std::runtime_error("Internal error in module::module()"); + pybind11_fail("Internal error in module::module()"); inc_ref(); } @@ -647,7 +646,7 @@ public: static module import(const char *name) { PyObject *obj = PyImport_ImportModule(name); if (!obj) - throw std::runtime_error("Module \"" + std::string(name) + "\" not found!"); + pybind11_fail("Module \"" + std::string(name) + "\" not found!"); return module(obj, false); } }; @@ -668,7 +667,7 @@ public: auto type = (PyHeapTypeObject*) type_holder.ptr(); if (!type_holder || !name) - throw std::runtime_error("generic_type: unable to create type object!"); + pybind11_fail("generic_type: unable to create type object!"); /* Register supplemental type information in C++ dict */ auto &internals = get_internals(); @@ -732,7 +731,7 @@ public: } if (PyType_Ready(&type->ht_type) < 0) - throw std::runtime_error("generic_type: PyType_Ready failed!"); + pybind11_fail("generic_type: PyType_Ready failed!"); m_ptr = type_holder.ptr(); @@ -756,7 +755,7 @@ protected: object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false); object name(PYBIND11_FROM_STRING(name_.c_str()), false); if (!type_holder || !name) - throw std::runtime_error("generic_type::metaclass(): unable to create type object!"); + pybind11_fail("generic_type::metaclass(): unable to create type object!"); auto type = (PyHeapTypeObject*) type_holder.ptr(); type->ht_name = name.release(); @@ -767,7 +766,7 @@ protected: ~Py_TPFLAGS_HAVE_GC; if (PyType_Ready(&type->ht_type) < 0) - throw std::runtime_error("generic_type::metaclass(): PyType_Ready failed!"); + pybind11_fail("generic_type::metaclass(): PyType_Ready failed!"); ob_type = (PyTypeObject *) type_holder.release(); } @@ -798,7 +797,7 @@ protected: auto ®istered_instances = detail::get_internals().registered_instances; auto it = registered_instances.find(self->value); if (it == registered_instances.end()) - throw std::runtime_error("generic_type::dealloc(): Tried to deallocate unregistered instance!"); + pybind11_fail("generic_type::dealloc(): Tried to deallocate unregistered instance!"); registered_instances.erase(it); } Py_XDECREF(self->parent); @@ -1096,14 +1095,12 @@ PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject * handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret); if (!nurse || !patient) - throw std::runtime_error("Could not activate keep_alive!"); + pybind11_fail("Could not activate keep_alive!"); cpp_function disable_lifesupport( [patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); }); weakref wr(nurse, disable_lifesupport); - if (!wr) - throw std::runtime_error("Could not allocate weak reference!"); patient.inc_ref(); /* reference patient and leak the weak reference */ (void) wr.release(); @@ -1138,7 +1135,7 @@ template void implicitly_convertible() auto & registered_types = detail::get_internals().registered_types_cpp; auto it = registered_types.find(&typeid(OutputType)); if (it == registered_types.end()) - throw std::runtime_error("implicitly_convertible: Unable to find type " + type_id()); + pybind11_fail("implicitly_convertible: Unable to find type " + type_id()); ((detail::type_info *) it->second)->implicit_conversions.push_back(implicit_caster); } @@ -1196,7 +1193,7 @@ inline function get_overload(const void *this_ptr, const char *name) { #define PYBIND11_OVERLOAD_PURE(ret_type, class_name, name, ...) \ PYBIND11_OVERLOAD_INT(ret_type, class_name, name, __VA_ARGS__) \ - throw std::runtime_error("Tried to call pure virtual function \"" #name "\""); + pybind11::pybind11_fail("Tried to call pure virtual function \"" #name "\""); NAMESPACE_END(pybind11) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 1b2c44005..8f5247185 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -88,12 +88,12 @@ public: iterator(PyObject *obj, bool borrowed = false) : object(obj, borrowed) { ++*this; } iterator& operator++() { if (ptr()) - value = object(PyIter_Next(ptr()), false); + value = object(PyIter_Next(m_ptr), false); return *this; } bool operator==(const iterator &it) const { return *it == **this; } bool operator!=(const iterator &it) const { return *it != **this; } - const object &operator*() const { return value; } + const handle &operator*() const { return value; } bool check() const { return PyIter_Check(ptr()); } private: object value; @@ -127,10 +127,10 @@ public: void operator=(const handle &h) { if (attr) { if (PyObject_SetAttr(obj, key, (PyObject *) h.ptr()) < 0) - throw std::runtime_error("Unable to set object attribute"); + pybind11_fail("Unable to set object attribute"); } else { if (PyObject_SetItem(obj, key, (PyObject *) h.ptr()) < 0) - throw std::runtime_error("Unable to set object item"); + pybind11_fail("Unable to set object item"); } } @@ -164,12 +164,12 @@ public: void operator=(const handle &o) { o.inc_ref(); // PyList_SetItem steals a reference if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0) - throw std::runtime_error("Unable to assign value in Python list!"); + pybind11_fail("Unable to assign value in Python list!"); } operator object() const { PyObject *result = PyList_GetItem(list, (ssize_t) index); if (!result) - throw std::runtime_error("Unable to retrieve value from Python list!"); + pybind11_fail("Unable to retrieve value from Python list!"); return object(result, true); } private: @@ -184,12 +184,12 @@ public: void operator=(const handle &o) { o.inc_ref(); // PyTuple_SetItem steals a reference if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0) - throw std::runtime_error("Unable to assign value in Python tuple!"); + pybind11_fail("Unable to assign value in Python tuple!"); } operator object() const { PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index); if (!result) - throw std::runtime_error("Unable to retrieve value from Python tuple!"); + pybind11_fail("Unable to retrieve value from Python tuple!"); return object(result, true); } private: @@ -205,8 +205,8 @@ public: pos = -1; return *this; } - std::pair operator*() const { - return std::make_pair(object(key, true), object(value, true)); + std::pair operator*() const { + return std::make_pair(key, value); } bool operator==(const dict_iterator &it) const { return it.pos == pos; } bool operator!=(const dict_iterator &it) const { return it.pos != pos; } @@ -242,7 +242,10 @@ inline iterator handle::end() const { return iterator(nullptr); } class str : public object { public: PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check) - str(const std::string &s) : object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) { } + str(const std::string &s) + : object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) { + if (!m_ptr) pybind11_fail("Could not allocate string object!"); + } operator std::string() const { #if PY_MAJOR_VERSION >= 3 @@ -250,7 +253,7 @@ public: #else object temp(PyUnicode_AsUTF8String(m_ptr), false); if (temp.ptr() == nullptr) - throw std::runtime_error("Unable to extract string contents!"); + pybind11_fail("Unable to extract string contents!"); return PyString_AsString(temp.ptr()); #endif } @@ -270,14 +273,16 @@ public: PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK) bytes(const std::string &s) - : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), s.size()), false) { } + : object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), s.size()), false) { + if (!m_ptr) pybind11_fail("Could not allocate bytes object!"); + } operator std::string() const { char *buffer; ssize_t length; int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length); if (err == -1) - throw std::runtime_error("Unable to extract bytes contents!"); + pybind11_fail("Unable to extract bytes contents!"); return std::string(buffer, length); } }; @@ -306,6 +311,7 @@ public: else m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value); } + if (!m_ptr) pybind11_fail("Could not allocate int object!"); } template operator T *() const { T * result = static_cast(PyCapsule_GetPointer(m_ptr, nullptr)); - if (!result) throw std::runtime_error("Unable to extract capsule contents!"); + if (!result) pybind11_fail("Unable to extract capsule contents!"); return result; } }; @@ -368,7 +384,9 @@ public: class tuple : public object { public: PYBIND11_OBJECT(tuple, object, PyTuple_Check) - tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) { } + tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) { + if (!m_ptr) pybind11_fail("Could not allocate tuple object!"); + } size_t size() const { return (size_t) PyTuple_Size(m_ptr); } detail::tuple_accessor operator[](size_t index) const { return detail::tuple_accessor(ptr(), index); } }; @@ -376,7 +394,9 @@ public: class dict : public object { public: PYBIND11_OBJECT(dict, object, PyDict_Check) - dict() : object(PyDict_New(), false) { } + dict() : object(PyDict_New(), false) { + if (!m_ptr) pybind11_fail("Could not allocate dict object!"); + } size_t size() const { return (size_t) PyDict_Size(m_ptr); } detail::dict_iterator begin() const { return (++detail::dict_iterator(ptr(), 0)); } detail::dict_iterator end() const { return detail::dict_iterator(); } @@ -386,7 +406,9 @@ public: class list : public object { public: PYBIND11_OBJECT(list, object, PyList_Check) - list(size_t size = 0) : object(PyList_New((ssize_t) size), false) { } + list(size_t size = 0) : object(PyList_New((ssize_t) size), false) { + if (!m_ptr) pybind11_fail("Could not allocate list object!"); + } size_t size() const { return (size_t) PyList_Size(m_ptr); } detail::list_accessor operator[](size_t index) const { return detail::list_accessor(ptr(), index); } void append(const object &object) const { PyList_Append(m_ptr, (PyObject *) object.ptr()); } @@ -395,10 +417,12 @@ public: class set : public object { public: PYBIND11_OBJECT(set, object, PySet_Check) - set() : object(PySet_New(nullptr), false) { } + set() : object(PySet_New(nullptr), false) { + if (!m_ptr) pybind11_fail("Could not allocate set object!"); + } size_t size() const { return (size_t) PySet_Size(m_ptr); } - void add(const object &object) const { PySet_Add(m_ptr, (PyObject *) object.ptr()); } - void clear() const { PySet_Clear(ptr()); } + bool add(const object &object) const { return PySet_Add(m_ptr, (PyObject *) object.ptr()) == 0; } + void clear() const { PySet_Clear(m_ptr); } }; class function : public object { @@ -448,7 +472,7 @@ PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { return (detail::type_info *) it->second; type = type->tp_base; if (type == nullptr) - throw std::runtime_error("pybind11::detail::get_type_info: unable to find type object!"); + pybind11_fail("pybind11::detail::get_type_info: unable to find type object!"); } while (true); } diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 3f3da7f30..c57befaf8 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -14,7 +14,6 @@ #include #include - #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4127) // warning C4127: Conditional expression is constant @@ -28,14 +27,14 @@ template struct type_caster value_conv; public: bool load(PyObject *src, bool convert) { - if (!PyList_Check(src)) + list l(src, true); + if (!l.check()) return false; - size_t size = (size_t) PyList_GET_SIZE(src); - value.reserve(size); + value.reserve(l.size()); value.clear(); value_conv conv; - for (size_t i=0; i")); }; @@ -68,8 +65,8 @@ public: return false; value.clear(); key_conv conv; - for (const object &o: s) { - if (!conv.load((PyObject *) o.ptr(), convert)) + for (auto entry : s) { + if (!conv.load(entry.ptr(), convert)) return false; value.insert((Key) conv); } @@ -77,15 +74,13 @@ public: } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { - object set(PySet_New(nullptr), false); - if (!set) - return nullptr; + pybind11::set s; for (auto const &value: src) { object value_(key_conv::cast(value, policy, parent), false); - if (!value_ || PySet_Add(set.ptr(), value_.ptr()) != 0) + if (!value_ || !s.add(value)) return nullptr; } - return set.release(); + return s.release(); } PYBIND11_TYPE_CASTER(type, _("set<") + key_conv::name() + _(">")); }; @@ -97,16 +92,15 @@ public: typedef type_caster value_conv; bool load(PyObject *src, bool convert) { - if (!PyDict_Check(src)) + dict d(src, true); + if (!d.check()) return false; - - value.clear(); - PyObject *key_, *value_; - ssize_t pos = 0; key_conv kconv; value_conv vconv; - while (PyDict_Next(src, &pos, &key_, &value_)) { - if (!kconv.load(key_, convert) || !vconv.load(value_, convert)) + value.clear(); + for (auto it : d) { + if (!kconv.load(it.first.ptr(), convert) || + !vconv.load(it.second.ptr(), convert)) return false; value[(Key) kconv] = (Value) vconv; } @@ -114,16 +108,15 @@ public: } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { - object dict(PyDict_New(), false); - if (!dict) - return nullptr; + dict d; for (auto const &kv: src) { object key(key_conv::cast(kv.first, policy, parent), false); object value(value_conv::cast(kv.second, policy, parent), false); - if (!key || !value || PyDict_SetItem(dict.ptr(), key.ptr(), value.ptr()) != 0) + if (!key || !value) return nullptr; + d[key] = value; } - return dict.release(); + return d.release(); } PYBIND11_TYPE_CASTER(type, _("dict<") + key_conv::name() + _(", ") + value_conv::name() + _(">")); @@ -131,7 +124,10 @@ public: NAMESPACE_END(detail) -inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (std::string) obj.str(); return os; } +inline std::ostream &operator<<(std::ostream &os, const handle &obj) { + os << (std::string) obj.str(); + return os; +} NAMESPACE_END(pybind11) diff --git a/include/pybind11/typeid.h b/include/pybind11/typeid.h index 2d7c39bbd..b395110f6 100644 --- a/include/pybind11/typeid.h +++ b/include/pybind11/typeid.h @@ -11,6 +11,7 @@ #include #include + #if defined(__GNUG__) #include #endif @@ -26,7 +27,7 @@ inline void erase_all(std::string &string, const std::string &search) { } } -inline void clean_type_id(std::string &name) { +PYBIND11_NOINLINE inline void clean_type_id(std::string &name) { #if defined(__GNUG__) int status = 0; std::unique_ptr res { diff --git a/setup.py b/setup.py index 17ef50974..691a51a53 100644 --- a/setup.py +++ b/setup.py @@ -17,6 +17,7 @@ setup( headers=[ 'include/pybind11/cast.h', 'include/pybind11/complex.h', + 'include/pybind11/descr.h', 'include/pybind11/numpy.h', 'include/pybind11/pybind11.h', 'include/pybind11/stl.h', @@ -57,11 +58,10 @@ C++11-compatible compilers are widely available, this heavy machinery has become an excessively large and unnecessary dependency. Think of this library as a tiny self-contained version of Boost.Python with -everything stripped away that isn't relevant for binding generation. The whole -codebase requires less than 3000 lines of code and only depends on Python (2.7 -or 3.x) and the C++ standard library. This compact implementation was -possible thanks to some of the new C++11 language features (tuples, lambda -functions and variadic templates). Since its creation, this library has -grown beyond Boost.Python in many ways, leading to dramatically simpler binding -code in many common situations.""", -) +everything stripped away that isn't relevant for binding generation. The core +header files only require ~2.5K lines of code and depend on Python (2.7 or 3.x) +and the C++ standard library. This compact implementation was possible thanks +to some of the new C++11 language features (specifically: tuples, lambda +functions and variadic templates). Since its creation, this library has grown +beyond Boost.Python in many ways, leading to dramatically simpler binding code +in many common situations.""")