From ed3d23a5d6456782522aa45027c44314ccdbff10 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 17 Jan 2016 22:36:44 +0100 Subject: [PATCH] major cleanup of the whole codebase - new pybind11::base<> attribute to indicate a subclass relationship - unified infrastructure for parsing variadic arguments in class_ and cpp_function - use 'handle' and 'object' more consistently everywhere --- CMakeLists.txt | 3 +- docs/advanced.rst | 2 +- docs/changelog.rst | 14 +- docs/classes.rst | 30 +- docs/cmake.rst | 2 +- docs/reference.rst | 18 +- example/example5.cpp | 10 + example/example5.py | 4 + example/example5.ref | 22 +- include/pybind11/attr.h | 275 +++++++++++ include/pybind11/cast.h | 367 ++++++++------- include/pybind11/common.h | 22 +- include/pybind11/complex.h | 6 +- include/pybind11/functional.h | 13 +- include/pybind11/numpy.h | 16 +- include/pybind11/operators.h | 8 +- include/pybind11/pybind11.h | 829 ++++++++++++++-------------------- include/pybind11/pytypes.h | 158 +++---- include/pybind11/stl.h | 32 +- setup.py | 15 +- 20 files changed, 996 insertions(+), 850 deletions(-) create mode 100644 include/pybind11/attr.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ba941f9ba..a0789417b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ include_directories(${PYTHON_INCLUDE_DIR}) include_directories(include) set(PYBIND11_HEADERS + include/pybind11/attr.h include/pybind11/cast.h include/pybind11/common.h include/pybind11/complex.h @@ -167,7 +168,7 @@ elseif (UNIX) # Strip unnecessary sections of the binary on Linux/Mac OS if(APPLE) set_target_properties(example PROPERTIES MACOSX_RPATH ".") - set_target_properties(example PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -dead_strip") + set_target_properties(example PROPERTIES LINK_FLAGS "-undefined dynamic_lookup ") if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_SOURCE_DIR}/example/example.so) endif() diff --git a/docs/advanced.rst b/docs/advanced.rst index 4c2b23bb2..4683c7086 100644 --- a/docs/advanced.rst +++ b/docs/advanced.rst @@ -925,7 +925,7 @@ If not available, the signature may not be very helpful, e.g.: FUNCTIONS ... | myFunction(...) - | Signature : (MyClass, arg : SomeType = ) -> None + | Signature : (MyClass, arg : SomeType = ) -> NoneType ... The first way of addressing this is by defining ``SomeType.__repr__``. diff --git a/docs/changelog.rst b/docs/changelog.rst index b9923037e..d497e26a7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -5,15 +5,19 @@ Changelog 1.2 (not yet released) -------------------------- + * Optional: efficient generation of function signatures at compile time using C++14 -* Switched to a simpler and more general way of dealing with function default arguments - Unused keyword arguments in function calls are now detected and cause errors as expected +* Switched to a simpler and more general way of dealing with function default + arguments. Unused keyword arguments in function calls are now detected and + cause errors as expected * New ``keep_alive`` call policy analogous to Boost.Python's ``with_custodian_and_ward`` +* New ``pybind11::base<>`` attribute to indicate a subclass relationship * Improved interface for RAII type wrappers in ``pytypes.h`` * Use RAII type wrappers consistently within pybind11 itself. This fixes various potential refcount leaks when exceptions occur * Added new ``bytes`` RAII type wrapper (maps to ``string`` in Python 2.7). -* Made handle and related RAII classes const correct +* Made handle and related RAII classes const correct, using them more + consistently everywhere now * Got rid of the ugly ``__pybind11__`` attributes on the Python side---they are now stored in a C++ hash table that is not visible in Python * Fixed refcount leaks involving NumPy arrays and bound functions @@ -21,7 +25,9 @@ Changelog * Removed an unnecessary copy operation in ``pybind11::vectorize`` * Fixed naming clashes when both pybind11 and NumPy headers are included * Added conversions for additional exception types -* Documentation improvements (using multiple extension modules, smart pointers, other minor clarifications) +* Documentation improvements (using multiple extension modules, smart pointers, + other minor clarifications) +* unified infrastructure for parsing variadic arguments in class_ and cpp_function * Fixed license text (was: ZLIB, should have been: 3-clause BSD) * Python 3.2 compatibility diff --git a/docs/classes.rst b/docs/classes.rst index 5aad4d1bf..cfe349fda 100644 --- a/docs/classes.rst +++ b/docs/classes.rst @@ -177,9 +177,22 @@ inheritance relationship: std::string bark() const { return "woof!"; } }; -To capture the hierarchical relationship in pybind11, we must assign a name to -the ``Pet`` :class:`class_` instance and reference it when binding the ``Dog`` -class. +There are two different ways of indicating a hierarchical relationship to +pybind11: the first is by specifying the C++ base class explicitly during +construction using the ``base`` attribute: + +.. code-block:: cpp + + py::class_(m, "Pet") + .def(py::init()) + .def_readwrite("name", &Pet::name); + + py::class_(m, "Dog", py::base() /* <- specify C++ parent type */) + .def(py::init()) + .def("bark", &Dog::bark); + +Alternatively, we can also assign a name to the previously bound ``Pet`` +:class:`class_` object and reference it when binding the ``Dog`` class: .. code-block:: cpp @@ -187,11 +200,12 @@ class. pet.def(py::init()) .def_readwrite("name", &Pet::name); - py::class_(m, "Dog", pet /* <- specify parent */) + py::class_(m, "Dog", pet /* <- specify Python parent type */) .def(py::init()) .def("bark", &Dog::bark); -Instances then expose fields and methods of both types: +Functionality-wise, both approaches are completely equivalent. Afterwards, +instances will expose fields and methods of both types: .. code-block:: python @@ -242,14 +256,14 @@ The overload signatures are also visible in the method's docstring: | Methods defined here: | | __init__(...) - | Signature : (Pet, str, int) -> None + | Signature : (Pet, str, int) -> NoneType | | set(...) - | 1. Signature : (Pet, int) -> None + | 1. Signature : (Pet, int) -> NoneType | | Set the pet's age | - | 2. Signature : (Pet, str) -> None + | 2. Signature : (Pet, str) -> NoneType | | Set the pet's name diff --git a/docs/cmake.rst b/docs/cmake.rst index 50d50b5d4..d24640d4a 100644 --- a/docs/cmake.rst +++ b/docs/cmake.rst @@ -115,7 +115,7 @@ and that the pybind11 repository is located in a subdirectory named :file:`pybin # Strip unnecessary sections of the binary on Linux/Mac OS if(APPLE) set_target_properties(example PROPERTIES MACOSX_RPATH ".") - set_target_properties(example PROPERTIES LINK_FLAGS "-undefined dynamic_lookup -dead_strip") + set_target_properties(example PROPERTIES LINK_FLAGS "-undefined dynamic_lookup ") if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_BINARY_DIR}/example.so) endif() diff --git a/docs/reference.rst b/docs/reference.rst index ab5c11b68..4df4344a8 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -63,17 +63,19 @@ Without reference counting Return the ``PyObject *`` underlying a :class:`handle`. -.. function:: void handle::inc_ref() const +.. function:: const handle& handle::inc_ref() const Manually increase the reference count of the Python object. Usually, it is preferable to use the :class:`object` class which derives from - :class:`handle` and calls this function automatically. + :class:`handle` and calls this function automatically. Returns a reference + to itself. -.. function:: void handle::dec_ref() const +.. function:: const handle& handle::dec_ref() const Manually decrease the reference count of the Python object. Usually, it is preferable to use the :class:`object` class which derives from - :class:`handle` and calls this function automatically. + :class:`handle` and calls this function automatically. Returns a reference + to itself. .. function:: void handle::ref_count() const @@ -167,11 +169,11 @@ With reference counting Move constructor; steals the object from ``other`` and preserves its reference count. -.. function:: PyObject* object::release() +.. function:: handle object::release() - Release ownership of underlying ``PyObject *``. Returns raw Python object - pointer without decreasing its reference count and resets handle to - ``nullptr``-valued pointer. + Resets the internal pointer to ``nullptr`` without without decreasing the + object's reference count. The function returns a raw handle to the original + Python object. .. function:: object::~object() diff --git a/example/example5.cpp b/example/example5.cpp index cabd84795..ad24ef069 100644 --- a/example/example5.cpp +++ b/example/example5.cpp @@ -29,6 +29,11 @@ public: void bark() const { std::cout << "Woof!" << std::endl; } }; +class Rabbit : public Pet { +public: + Rabbit(const std::string &name) : Pet(name, "parrot") {} +}; + void pet_print(const Pet &pet) { std::cout << pet.name() + " is a " + pet.species() << std::endl; } @@ -62,9 +67,14 @@ void init_ex5(py::module &m) { .def("name", &Pet::name) .def("species", &Pet::species); + /* One way of declaring a subclass relationship: reference parent's class_ object */ py::class_(m, "Dog", pet_class) .def(py::init()); + /* Another way of declaring a subclass relationship: reference parent's C++ type */ + py::class_(m, "Rabbit", py::base()) + .def(py::init()); + m.def("pet_print", pet_print); m.def("dog_bark", dog_bark); diff --git a/example/example5.py b/example/example5.py index b7e3cd369..0699978d2 100755 --- a/example/example5.py +++ b/example/example5.py @@ -5,11 +5,15 @@ sys.path.append('.') from example import Pet from example import Dog +from example import Rabbit from example import dog_bark from example import pet_print polly = Pet('Polly', 'parrot') molly = Dog('Molly') +roger = Rabbit('Rabbit') +print(roger.name() + " is a " + roger.species()) +pet_print(roger) print(polly.name() + " is a " + polly.species()) pet_print(polly) print(molly.name() + " is a " + molly.species()) diff --git a/example/example5.ref b/example/example5.ref index 41544c8e6..d1da88311 100644 --- a/example/example5.ref +++ b/example/example5.ref @@ -1,16 +1,7 @@ +Rabbit is a parrot Polly is a parrot -Polly is a parrot -Molly is a dog Molly is a dog Woof! -The following error is expected: Incompatible function arguments. The following argument types are supported: - 1. (Dog) -> NoneType - -Callback function 1 called! -False -Callback function 2 called : Hello, x, True, 5 -5 -func(43) = 44 func(43) = 44 Payload constructor Payload copy constructor @@ -18,3 +9,14 @@ Payload move constructor Payload destructor Payload destructor Payload destructor +Rabbit is a parrot +Polly is a parrot +Molly is a dog +The following error is expected: Incompatible function arguments. The following argument types are supported: + 1. (example.Dog) -> NoneType + +Callback function 1 called! +False +Callback function 2 called : Hello, x, True, 5 +5 +func(43) = 44 diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h new file mode 100644 index 000000000..9a646cb61 --- /dev/null +++ b/include/pybind11/attr.h @@ -0,0 +1,275 @@ +/* + pybind11/pybind11.h: Infrastructure for processing custom + type and function attributes + + 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 "cast.h" + +NAMESPACE_BEGIN(pybind11) + +template struct arg_t; + +/// Annotation for keyword arguments +struct arg { + arg(const char *name) : name(name) { } + template arg_t operator=(const T &value); + const char *name; +}; + +/// Annotation for keyword arguments with default values +template struct arg_t : public arg { + arg_t(const char *name, const T &value, const char *descr = nullptr) + : arg(name), value(value), descr(descr) { } + T value; + const char *descr; +}; + +template arg_t arg::operator=(const T &value) { return arg_t(name, value); } + +/// Annotation for methods +struct is_method { handle class_; is_method(const handle &c) : class_(c) { } }; + +/// Annotation for documentation +struct doc { const char *value; doc(const char *value) : value(value) { } }; + +/// Annotation for function names +struct name { const char *value; name(const char *value) : value(value) { } }; + +/// Annotation indicating that a function is an overload associated with a given "sibling" +struct sibling { handle value; sibling(const handle &value) : value(value.ptr()) { } }; + +/// Annotation indicating that a class derives from another given type +template struct base { }; + +/// Keep patient alive while nurse lives +template struct keep_alive { }; + +NAMESPACE_BEGIN(detail) +/* Forward declarations */ +enum op_id : int; +enum op_type : int; +struct undefined_t; +template struct op_; +template struct init; +inline void keep_alive_impl(int Nurse, int Patient, handle args, handle ret); + +/// Internal data structure which holds metadata about a keyword argument +struct argument_record { + const char *name; ///< Argument name + const char *descr; ///< Human-readable version of the argument value + handle value; ///< Associated Python object + + argument_record(const char *name, const char *descr, handle value) + : name(name), descr(descr), value(value) { } +}; + +/// Internal data structure which holds metadata about a bound function (signature, overloads, etc.) +struct function_record { + /// Function name + char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ + + // User-specified documentation string + char *doc = nullptr; + + /// Human-readable version of the function signature + char *signature = nullptr; + + /// List of registered keyword arguments + std::vector args; + + /// Pointer to lambda function which converts arguments and performs the actual call + handle (*impl) (function_record *, handle, handle) = nullptr; + + /// Storage for the wrapped function pointer and captured data, if any + void *data = nullptr; + + /// Pointer to custom destructor for 'data' (if needed) + void (*free_data) (void *ptr) = nullptr; + + /// Return value policy associated with this function + return_value_policy policy = return_value_policy::automatic; + + /// True if name == '__init__' + bool is_constructor = false; + + /// Python method object + PyMethodDef *def = nullptr; + + /// Python handle to the associated class (if this is method) + handle class_; + + /// Python handle to the sibling function representing an overload chain + handle sibling; + + /// Pointer to next overload + function_record *next = nullptr; +}; + +/// Special data structure which (temporarily) holds metadata about a bound class +struct type_record { + /// Handle to the parent scope + handle scope; + + /// Name of the class + const char *name = nullptr; + + // Pointer to RTTI type_info data structure + const std::type_info *type = nullptr; + + /// How large is the underlying C++ type? + size_t type_size = 0; + + /// How large is pybind11::instance? + size_t instance_size = 0; + + /// Function pointer to class_<..>::init_holder + void (*init_holder)(PyObject *, const void *) = nullptr; + + /// Function pointer to class_<..>::dealloc + void (*dealloc)(PyObject *) = nullptr; + + // Pointer to RTTI type_info data structure of base class + const std::type_info *base_type = nullptr; + + /// OR: Python handle to base class + handle base_handle; + + /// Optional docstring + const char *doc = nullptr; +}; + +/** + * Partial template specializations to process custom attributes provided to + * cpp_function_ and class_. These are either used to initialize the respective + * fields in the type_record and function_record data structures or executed + * at runtime to deal with custom call policies (e.g. keep_alive). + */ +template struct process_attribute; + +template struct process_attribute_default { + /// Default implementation: do nothing + static void init(const T &, function_record *) { } + static void init(const T &, type_record *) { } + static void precall(handle) { } + static void postcall(handle, handle) { } +}; + +/// Process an attribute specifying the function's name +template <> struct process_attribute : process_attribute_default { + static void init(const name &n, function_record *r) { r->name = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring +template <> struct process_attribute : process_attribute_default { + static void init(const doc &n, function_record *r) { r->doc = const_cast(n.value); } +}; + +/// Process an attribute specifying the function's docstring (provided as a C-style string) +template <> struct process_attribute : process_attribute_default { + static void init(const char *d, function_record *r) { r->doc = const_cast(d); } + static void init(const char *d, type_record *r) { r->doc = const_cast(d); } +}; +template <> struct process_attribute : process_attribute { }; + +/// Process an attribute indicating the function's return value policy +template <> struct process_attribute : process_attribute_default { + static void init(const return_value_policy &p, function_record *r) { r->policy = p; } +}; + +/// Process an attribute which indicates that this is an overloaded function associated with a given sibling +template <> struct process_attribute : process_attribute_default { + static void init(const sibling &s, function_record *r) { r->sibling = s.value; } +}; + +/// Process an attribute which indicates that this function is a method +template <> struct process_attribute : process_attribute_default { + static void init(const is_method &s, function_record *r) { r->class_ = s.class_; } +}; + +/// Process a keyword argument attribute (*without* a default value) +template <> struct process_attribute : process_attribute_default { + static void init(const arg &a, function_record *r) { + if (r->class_ && r->args.empty()) + r->args.emplace_back("self", nullptr, handle()); + r->args.emplace_back(a.name, nullptr, handle()); + } +}; + +/// Process a keyword argument attribute (*with* a default value) +template +struct process_attribute> : process_attribute_default> { + static void init(const arg_t &a, function_record *r) { + if (r->class_ && r->args.empty()) + r->args.emplace_back("self", nullptr, handle()); + + /* Convert keyword value into a Python object */ + object o = object(detail::type_caster::type>::cast( + a.value, return_value_policy::automatic, handle()), false); + + if (!o) + pybind11_fail("arg(): could not convert default keyword " + "argument into a Python object (type not " + "registered yet?)"); + + r->args.emplace_back(a.name, a.descr, o.release()); + } +}; + +/// Process a parent class attribute +template +struct process_attribute::value>::type> : process_attribute_default { + static void init(const handle &h, type_record *r) { r->base_handle = h; } +}; + +/// Process a parent class attribute +template +struct process_attribute> : process_attribute_default> { + static void init(const base &, type_record *r) { r->base_type = &typeid(T); } +}; + +/*** + * Process a keep_alive call policy -- invokes keep_alive_impl during the + * pre-call handler if both Nurse, Patient != 0 and use the post-call handler + * otherwise + */ +template struct process_attribute> : public process_attribute_default> { + template ::type = 0> + static void precall(handle args) { keep_alive_impl(Nurse, Patient, args, handle()); } + template ::type = 0> + static void postcall(handle, handle) { } + template ::type = 0> + static void precall(handle) { } + template ::type = 0> + static void postcall(handle args, handle ret) { keep_alive_impl(Nurse, Patient, args, ret); } +}; + + +/// Recursively iterate over variadic template arguments +template struct process_attributes { + static void init(const Args&... args, function_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + (void) unused; + } + static void init(const Args&... args, type_record *r) { + int unused[] = { 0, (process_attribute::type>::init(args, r), 0) ... }; + (void) unused; + } + static void precall(handle fn_args) { + int unused[] = { 0, (process_attribute::type>::precall(fn_args), 0) ... }; + (void) unused; + } + static void postcall(handle fn_args, handle fn_ret) { + int unused[] = { 0, (process_attribute::type>::postcall(fn_args, fn_ret), 0) ... }; + (void) unused; + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind11) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index 8fab99d03..bc26e4777 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -19,95 +19,168 @@ NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(detail) -class type_caster_generic { -public: - PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) { - auto &types = get_internals().registered_types_cpp; +/// Additional type information which does not fit into the PyTypeObject +struct type_info { + PyTypeObject *type; + size_t type_size; + void (*init_holder)(PyObject *, const void *); + std::vector implicit_conversions; + buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; + void *get_buffer_data = nullptr; +}; - auto it = types.find(type_info); - if (it != types.end()) { - typeinfo = (detail::type_info *) it->second; - } else { - /* Unknown type?! Since std::type_info* often varies across - module boundaries, the following does an explicit check */ - for (auto const &type : types) { - auto *first = (const std::type_info *) type.first; - if (strcmp(first->name(), type_info->name()) == 0) { - types[type_info] = type.second; - typeinfo = (detail::type_info *) type.second; - break; - } +PYBIND11_NOINLINE inline internals &get_internals() { + static internals *internals_ptr = nullptr; + if (internals_ptr) + return *internals_ptr; + handle builtins(PyEval_GetBuiltins()); + capsule caps(builtins["__pybind11__"]); + if (caps.check()) { + internals_ptr = caps; + } else { + internals_ptr = new internals(); + builtins["__pybind11__"] = capsule(internals_ptr); + } + return *internals_ptr; +} + +PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { + auto const &type_dict = get_internals().registered_types_py; + do { + auto it = type_dict.find(type); + if (it != type_dict.end()) + return (detail::type_info *) it->second; + type = type->tp_base; + if (!type) + pybind11_fail("pybind11::detail::get_type_info: unable to find type object!"); + } while (true); +} + +PYBIND11_NOINLINE inline detail::type_info *get_type_info(const std::type_info &tp) { + auto &types = get_internals().registered_types_cpp; + + auto it = types.find(&tp); + if (it != types.end()) { + return (detail::type_info *) it->second; + } else { + /* Unknown type?! Since std::type_info* often varies across + module boundaries, the following does an explicit check */ + for (auto const &type : types) { + auto *first = (const std::type_info *) type.first; + if (strcmp(first->name(), tp.name()) == 0) { + types[&tp] = type.second; + return (detail::type_info *) type.second; } } } + return nullptr; +} - PYBIND11_NOINLINE bool load(PyObject *src, bool convert) { - if (src == nullptr || typeinfo == nullptr) +PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp) { + detail::type_info *type_info = get_type_info(tp); + return handle(type_info ? ((PyObject *) type_info->type) : nullptr); +} + +PYBIND11_NOINLINE inline std::string error_string() { + std::string errorString; + PyThreadState *tstate = PyThreadState_GET(); + if (tstate == nullptr) + return ""; + + if (tstate->curexc_type) { + errorString += (std::string) handle(tstate->curexc_type).str(); + errorString += ": "; + } + if (tstate->curexc_value) + errorString += (std::string) handle(tstate->curexc_value).str(); + + return errorString; +} + +PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr) { + auto instances = get_internals().registered_instances; + auto it = instances.find(ptr); + if (it == instances.end()) + return handle(); + return handle((PyObject *) it->second); +} + +class type_caster_generic { +public: + PYBIND11_NOINLINE type_caster_generic(const std::type_info &type_info) + : typeinfo(get_type_info(type_info)) { } + + PYBIND11_NOINLINE bool load(handle src, bool convert) { + if (!src || !typeinfo) return false; - if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) { - value = ((instance *) src)->value; + if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) { + value = ((instance *) src.ptr())->value; return true; } if (convert) { for (auto &converter : typeinfo->implicit_conversions) { - temp = object(converter(src, typeinfo->type), false); - if (load(temp.ptr(), false)) + temp = object(converter(src.ptr(), typeinfo->type), false); + if (load(temp, false)) return true; } } return false; } - PYBIND11_NOINLINE static PyObject *cast(const void *_src, return_value_policy policy, PyObject *parent, - const std::type_info *type_info, - void *(*copy_constructor)(const void *), - const void *existing_holder = nullptr) { + PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent, + const std::type_info *type_info, + void *(*copy_constructor)(const void *), + const void *existing_holder = nullptr) { void *src = const_cast(_src); - if (src == nullptr) { - Py_INCREF(Py_None); - return Py_None; - } + if (src == nullptr) + return handle(Py_None).inc_ref(); + // 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; + parent && ((instance *) parent.ptr())->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 = (PyObject *) it_instance->second; - Py_INCREF(inst); - return inst; - } + if (it_instance != internals.registered_instances.end() && !dont_cache) + return handle((PyObject *) it_instance->second).inc_ref(); + auto it = internals.registered_types_cpp.find(type_info); if (it == internals.registered_types_cpp.end()) { std::string tname = type_info->name(); detail::clean_type_id(tname); std::string msg = "Unregistered type : " + tname; PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; + return handle(); } + auto tinfo = (const detail::type_info *) it->second; - instance *inst = (instance *) PyType_GenericAlloc(tinfo->type, 0); - inst->value = src; - inst->owned = true; - inst->parent = nullptr; + object inst(PyType_GenericAlloc(tinfo->type, 0), false); + + auto wrapper = (instance *) inst.ptr(); + + wrapper->value = src; + wrapper->owned = true; + wrapper->parent = nullptr; + if (policy == return_value_policy::automatic) policy = return_value_policy::take_ownership; + if (policy == return_value_policy::copy) { - inst->value = copy_constructor(inst->value); - if (inst->value == nullptr) + wrapper->value = copy_constructor(wrapper->value); + if (wrapper->value == nullptr) throw cast_error("return_value_policy = copy, but the object is non-copyable!"); } else if (policy == return_value_policy::reference) { - inst->owned = false; + wrapper->owned = false; } else if (policy == return_value_policy::reference_internal) { - inst->owned = false; - inst->parent = parent; - Py_XINCREF(parent); + wrapper->owned = false; + wrapper->parent = parent.inc_ref().ptr(); } - PyObject *inst_pyobj = (PyObject *) inst; - tinfo->init_holder(inst_pyobj, existing_holder); + + tinfo->init_holder(inst.ptr(), existing_holder); if (!dont_cache) - internals.registered_instances[inst->value] = inst_pyobj; - return inst_pyobj; + internals.registered_instances[wrapper->value] = inst.ptr(); + + return inst.release(); } protected: @@ -121,15 +194,15 @@ template class type_caster : public type public: static PYBIND11_DESCR name() { return type_descr(_()); } - type_caster() : type_caster_generic(&typeid(type)) { } + type_caster() : type_caster_generic(typeid(type)) { } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const type &src, return_value_policy policy, handle parent) { if (policy == return_value_policy::automatic) policy = return_value_policy::copy; return type_caster_generic::cast(&src, policy, parent, &typeid(type), ©_constructor); } - static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { + static handle cast(const type *src, return_value_policy policy, handle parent) { return type_caster_generic::cast(src, policy, parent, &typeid(type), ©_constructor); } @@ -149,7 +222,7 @@ protected: type value; \ public: \ static PYBIND11_DESCR name() { return type_descr(py_name); } \ - static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \ + static handle cast(const type *src, return_value_policy policy, handle parent) { \ return cast(*src, policy, parent); \ } \ operator type*() { return &value; } \ @@ -171,21 +244,21 @@ struct type_caster< typedef typename std::conditional::value, double, _py_type_1>::type py_type; public: - bool load(PyObject *src, bool) { + bool load(handle src, bool) { py_type py_value; if (std::is_floating_point::value) { - py_value = (py_type) PyFloat_AsDouble(src); + py_value = (py_type) PyFloat_AsDouble(src.ptr()); } else if (sizeof(T) <= sizeof(long)) { if (std::is_signed::value) - py_value = (py_type) PyLong_AsLong(src); + py_value = (py_type) PyLong_AsLong(src.ptr()); else - py_value = (py_type) PyLong_AsUnsignedLong(src); + py_value = (py_type) PyLong_AsUnsignedLong(src.ptr()); } else { if (std::is_signed::value) - py_value = (py_type) PYBIND11_LONG_AS_LONGLONG(src); + py_value = (py_type) PYBIND11_LONG_AS_LONGLONG(src.ptr()); else - py_value = (py_type) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(src); + py_value = (py_type) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(src.ptr()); } if ((py_value == (py_type) -1 && PyErr_Occurred()) || @@ -200,7 +273,7 @@ public: return true; } - static PyObject *cast(T src, return_value_policy /* policy */, PyObject * /* parent */) { + static handle cast(T src, return_value_policy /* policy */, handle /* parent */) { if (std::is_floating_point::value) { return PyFloat_FromDouble((double) src); } else if (sizeof(T) <= sizeof(long)) { @@ -216,12 +289,13 @@ public: } } - static PyObject *cast(const T *src, return_value_policy policy, PyObject *parent) { + static handle cast(const T *src, return_value_policy policy, handle parent) { return cast(*src, policy, parent); } template ::value, int>::type = 0> static PYBIND11_DESCR name() { return type_descr(_("int")); } + template ::value, int>::type = 0> static PYBIND11_DESCR name() { return type_descr(_("float")); } @@ -234,10 +308,9 @@ protected: template <> class type_caster { public: - bool load(PyObject *, bool) { return false; } - static PyObject *cast(void_type, return_value_policy /* policy */, PyObject * /* parent */) { - Py_INCREF(Py_None); - return Py_None; + bool load(handle, bool) { return false; } + static handle cast(void_type, return_value_policy /* policy */, handle /* parent */) { + return handle(Py_None).inc_ref(); } PYBIND11_TYPE_CASTER(void_type, _("NoneType")); }; @@ -247,38 +320,36 @@ template <> class type_caster : public type_caster { 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; } + bool load(handle src, bool) { + if (src.ptr() == Py_True) { value = true; return true; } + else if (src.ptr() == 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; + static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) { + return handle(src ? Py_True : Py_False).inc_ref(); } PYBIND11_TYPE_CASTER(bool, _("bool")); }; template <> class type_caster { public: - bool load(PyObject *src, bool) { + bool load(handle src, bool) { object temp; - PyObject *load_src = src; - if (PyUnicode_Check(src)) { - temp = object(PyUnicode_AsUTF8String(src), false); + handle load_src = src; + if (PyUnicode_Check(load_src.ptr())) { + temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false); if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError - load_src = temp.ptr(); + load_src = temp; } char *buffer; ssize_t length; - int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(load_src, &buffer, &length); + int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(load_src.ptr(), &buffer, &length); if (err == -1) { PyErr_Clear(); return false; } // TypeError value = std::string(buffer, length); return true; } - static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) { + static handle cast(const std::string &src, return_value_policy /* policy */, handle /* parent */) { return PyUnicode_FromStringAndSize(src.c_str(), src.length()); } @@ -287,25 +358,25 @@ public: template <> class type_caster { public: - bool load(PyObject *src, bool) { + bool load(handle src, bool) { object temp; - PyObject *load_src = src; - if (PyUnicode_Check(src)) { - temp = object(PyUnicode_AsUTF8String(src), false); + handle load_src = src; + if (PyUnicode_Check(load_src.ptr())) { + temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false); if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError - load_src = temp.ptr(); + load_src = temp; } - const char *ptr = PYBIND11_BYTES_AS_STRING(load_src); + const char *ptr = PYBIND11_BYTES_AS_STRING(load_src.ptr()); if (!ptr) { PyErr_Clear(); return false; } // TypeError value = std::string(ptr); return true; } - static PyObject *cast(const char *src, return_value_policy /* policy */, PyObject * /* parent */) { + static handle cast(const char *src, return_value_policy /* policy */, handle /* parent */) { return PyUnicode_FromString(src); } - static PyObject *cast(char src, return_value_policy /* policy */, PyObject * /* parent */) { + static handle cast(char src, return_value_policy /* policy */, handle /* parent */) { char str[2] = { src, '\0' }; return PyUnicode_DecodeLatin1(str, 1, nullptr); } @@ -321,25 +392,22 @@ protected: template class type_caster> { typedef std::pair type; public: - bool load(PyObject *src, bool convert) { - if (!PyTuple_Check(src) || PyTuple_Size(src) != 2) + bool load(handle src, bool convert) { + if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != 2) return false; - if (!first.load(PyTuple_GET_ITEM(src, 0), convert)) - return false; - return second.load(PyTuple_GET_ITEM(src, 1), convert); + return first.load(PyTuple_GET_ITEM(src.ptr(), 0), convert) && + second.load(PyTuple_GET_ITEM(src.ptr(), 1), convert); } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { - object o1(type_caster::type>::cast(src.first, policy, parent), false); - object o2(type_caster::type>::cast(src.second, policy, parent), false); + static handle cast(const type &src, return_value_policy policy, handle parent) { + object o1 = object(type_caster::type>::cast(src.first, policy, parent), false); + object o2 = object(type_caster::type>::cast(src.second, policy, parent), false); if (!o1 || !o2) - return nullptr; - PyObject *tuple = PyTuple_New(2); - if (!tuple) - return nullptr; - PyTuple_SET_ITEM(tuple, 0, o1.release()); - PyTuple_SET_ITEM(tuple, 1, o2.release()); - return tuple; + return handle(); + tuple result(2); + PyTuple_SET_ITEM(result.ptr(), 0, o1.release().ptr()); + PyTuple_SET_ITEM(result.ptr(), 1, o2.release().ptr()); + return result.release(); } static PYBIND11_DESCR name() { @@ -361,11 +429,11 @@ template class type_caster> { public: enum { size = sizeof...(Tuple) }; - bool load(PyObject *src, bool convert) { + bool load(handle src, bool convert) { return load(src, convert, typename make_index_sequence::type()); } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const type &src, return_value_policy policy, handle parent) { return cast(src, policy, parent, typename make_index_sequence::type()); } @@ -398,36 +466,32 @@ protected: return type((Tuple) std::get(value)...); } - template bool load(PyObject *src, bool convert, index_sequence) { - if (!PyTuple_Check(src)) + template bool load(handle src, bool convert, index_sequence) { + if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != size) 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)... + std::array success {{ + (PyTuple_GET_ITEM(src.ptr(), Indices) != nullptr ? std::get(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert) : false)... }}; (void) convert; /* avoid a warning when the tuple is empty */ - for (bool r : results) + for (bool r : success) 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 {{ + template static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence) { + std::array entries {{ object(type_caster::type>::cast(std::get(src), policy, parent), false)... }}; - for (const auto & result : results) - if (!result) - return nullptr; - PyObject *tuple = PyTuple_New(size); - if (!tuple) - return nullptr; + for (const auto &entry: entries) + if (!entry) + return handle(); + tuple result(size); int counter = 0; - for (auto & result : results) - PyTuple_SET_ITEM(tuple, counter++, result.release()); - return tuple; + for (auto & entry: entries) + PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr()); + return result.release(); } protected: @@ -443,19 +507,21 @@ public: using type_caster::temp; using type_caster::copy_constructor; - bool load(PyObject *src, bool convert) { - if (src == nullptr || typeinfo == nullptr) + bool load(handle src, bool convert) { + if (!src || !typeinfo) return false; - if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) { - auto inst = (instance *) src; + + if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) { + auto inst = (instance *) src.ptr(); value = inst->value; holder = inst->holder; return true; } + if (convert) { for (auto &converter : typeinfo->implicit_conversions) { - temp = object(converter(src, typeinfo->type), false); - if (load(temp.ptr(), false)) + temp = object(converter(src.ptr(), typeinfo->type), false); + if (load(temp, false)) return true; } } @@ -467,7 +533,7 @@ public: explicit operator holder_type&() { return holder; } explicit operator holder_type*() { return &holder; } - static PyObject *cast(const holder_type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const holder_type &src, return_value_policy policy, handle parent) { return type_caster_generic::cast( src.get(), policy, parent, &typeid(type), ©_constructor, &src); } @@ -483,27 +549,27 @@ template struct type_caster::value>::type> { public: template ::value, int>::type = 0> - bool load(PyObject *src, bool /* convert */) { value = handle(src); return value.check(); } + bool load(handle src, bool /* convert */) { value = src; return value.check(); } template ::value, int>::type = 0> - bool load(PyObject *src, bool /* convert */) { value = type(src, true); return value.check(); } + bool load(handle src, bool /* convert */) { value = type(src, true); return value.check(); } - static PyObject *cast(const handle &src, return_value_policy /* policy */, PyObject * /* parent */) { - src.inc_ref(); return (PyObject *) src.ptr(); + static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) { + return src.inc_ref(); } PYBIND11_TYPE_CASTER(type, handle_type_name::name()); }; NAMESPACE_END(detail) -template inline T cast(PyObject *object) { +template inline T cast(handle handle) { detail::type_caster::type> conv; - if (!conv.load(object, true)) + if (!conv.load(handle, 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) { +template inline object cast(const T &value, return_value_policy policy = return_value_policy::automatic, handle parent = handle()) { 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); @@ -514,23 +580,22 @@ template <> inline void handle::cast() const { return; } template inline object handle::call(Args&&... args_) const { const size_t size = sizeof...(Args); - std::array args{ + std::array args { { object(detail::type_caster::type>::cast( std::forward(args_), return_value_policy::reference, nullptr), false)... } }; - for (const auto & result : args) - if (!result) - throw cast_error("handle::call(): unable to convert input arguments to Python objects"); - object tuple(PyTuple_New(size), false); - if (!tuple) - throw cast_error("handle::call(): unable to allocate tuple"); + for (auto &arg_value : args) + if (!arg_value) + throw cast_error("handle::call(): unable to convert input " + "arguments to Python objects"); + tuple args_tuple(size); int counter = 0; - for (auto & result : args) - PyTuple_SET_ITEM(tuple.ptr(), counter++, result.release()); - PyObject *result = PyObject_CallObject(m_ptr, tuple.ptr()); - if (result == nullptr && PyErr_Occurred()) + for (auto &arg_value : args) + PyTuple_SET_ITEM(args_tuple.ptr(), counter++, arg_value.release().ptr()); + object result(PyObject_CallObject(m_ptr, args_tuple.ptr()), false); + if (!result) throw error_already_set(); - return object(result, false); + return result; } NAMESPACE_END(pybind11) diff --git a/include/pybind11/common.h b/include/pybind11/common.h index ff84661d2..a0af6787d 100644 --- a/include/pybind11/common.h +++ b/include/pybind11/common.h @@ -73,6 +73,7 @@ #include #if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyInstanceMethod_New(ptr) #define PYBIND11_BYTES_CHECK PyBytes_Check #define PYBIND11_BYTES_FROM_STRING PyBytes_FromString #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize @@ -89,6 +90,7 @@ #define PYBIND11_PLUGIN_IMPL(name) \ extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() #else +#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_) #define PYBIND11_BYTES_CHECK PyString_Check #define PYBIND11_BYTES_FROM_STRING PyString_FromString #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize @@ -200,16 +202,6 @@ template > struct in holder_type holder; }; -/// Additional type information which does not fit into the PyTypeObject -struct type_info { - PyTypeObject *type; - size_t type_size; - void (*init_holder)(PyObject *, const void *); - std::vector implicit_conversions; - buffer_info *(*get_buffer)(PyObject *, void *) = nullptr; - void *get_buffer_data = nullptr; -}; - struct overload_hash { inline std::size_t operator()(const std::pair& v) const { size_t value = std::hash()(v.first); @@ -218,16 +210,6 @@ struct overload_hash { } }; -/// Stores information about a keyword argument -struct argument_entry { - const char *name; ///< Argument name - const char *descr; ///< Human-readable version of the argument value - PyObject *value; ///< Associated Python object - - argument_entry(const char *name, const char *descr, PyObject *value) - : name(name), descr(descr), value(value) { } -}; - /// Internal data struture used to track registered instances and types struct internals { std::unordered_map registered_types_cpp; // std::type_info* -> type_info diff --git a/include/pybind11/complex.h b/include/pybind11/complex.h index be0614c59..10da21d02 100644 --- a/include/pybind11/complex.h +++ b/include/pybind11/complex.h @@ -20,8 +20,8 @@ PYBIND11_DECL_FMT(std::complex, "Zd"); NAMESPACE_BEGIN(detail) template class type_caster> { public: - bool load(PyObject *src, bool) { - Py_complex result = PyComplex_AsCComplex(src); + bool load(handle src, bool) { + Py_complex result = PyComplex_AsCComplex(src.ptr()); if (result.real == -1.0 && PyErr_Occurred()) { PyErr_Clear(); return false; @@ -30,7 +30,7 @@ public: return true; } - static PyObject *cast(const std::complex &src, return_value_policy /* policy */, PyObject * /* parent */) { + static handle cast(const std::complex &src, return_value_policy /* policy */, handle /* parent */) { return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); } diff --git a/include/pybind11/functional.h b/include/pybind11/functional.h index 5bc898834..95ac7c92c 100644 --- a/include/pybind11/functional.h +++ b/include/pybind11/functional.h @@ -18,14 +18,13 @@ NAMESPACE_BEGIN(detail) template struct type_caster> { typedef std::function type; public: - - bool load(PyObject *src_, bool) { + bool load(handle src_, bool) { src_ = detail::get_function(src_); - if (!src_ || !(PyFunction_Check(src_) || PyCFunction_Check(src_))) + if (!src_ || !(PyFunction_Check(src_.ptr()) || PyCFunction_Check(src_.ptr()))) return false; object src(src_, true); value = [src](Args... args) -> Return { - object retval(pybind11::handle(src).call(std::move(args)...)); + object retval(src.call(std::move(args)...)); /* Visual studio 2015 parser issue: need parentheses around this expression */ return (retval.template cast()); }; @@ -33,10 +32,8 @@ public: } template - static PyObject *cast(Func &&f_, return_value_policy policy, PyObject *) { - cpp_function f(std::forward(f_), policy); - f.inc_ref(); - return f.ptr(); + static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) { + return cpp_function(std::forward(f_), policy).release(); } PYBIND11_TYPE_CASTER(type, _("function<") + diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h index 2365a6086..0aaac96ef 100644 --- a/include/pybind11/numpy.h +++ b/include/pybind11/numpy.h @@ -79,36 +79,36 @@ public: API& api = lookup_api(); PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor::value); if (descr == nullptr) - throw std::runtime_error("NumPy: unsupported buffer format!"); + pybind11_fail("NumPy: unsupported buffer format!"); Py_intptr_t shape = (Py_intptr_t) size; object tmp = object(api.PyArray_NewFromDescr_( api.PyArray_Type_, descr, 1, &shape, nullptr, (void *) ptr, 0, nullptr), false); if (ptr && tmp) tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); if (!tmp) - throw std::runtime_error("NumPy: unable to create array!"); - m_ptr = tmp.release(); + pybind11_fail("NumPy: unable to create array!"); + m_ptr = tmp.release().ptr(); } array(const buffer_info &info) { API& api = lookup_api(); if ((info.format.size() < 1) || (info.format.size() > 2)) - throw std::runtime_error("Unsupported buffer format!"); + pybind11_fail("Unsupported buffer format!"); int fmt = (int) info.format[0]; if (info.format == "Zd") fmt = API::NPY_CDOUBLE_; else if (info.format == "Zf") fmt = API::NPY_CFLOAT_; PyObject *descr = api.PyArray_DescrFromType_(fmt); if (descr == nullptr) - throw std::runtime_error("NumPy: unsupported buffer format '" + info.format + "'!"); + pybind11_fail("NumPy: unsupported buffer format '" + info.format + "'!"); object tmp(api.PyArray_NewFromDescr_( api.PyArray_Type_, descr, info.ndim, (Py_intptr_t *) &info.shape[0], (Py_intptr_t *) &info.strides[0], info.ptr, 0, nullptr), false); if (info.ptr && tmp) tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); if (!tmp) - throw std::runtime_error("NumPy: unable to create array!"); - m_ptr = tmp.release(); + pybind11_fail("NumPy: unable to create array!"); + m_ptr = tmp.release().ptr(); } protected: @@ -186,7 +186,7 @@ struct vectorize_helper { /* Check if the parameters are actually compatible */ for (size_t i=0; i struct op_impl { } /// Operator implementation generator template struct op_ { - template void execute(pybind11::class_ &class_, Extra&&... extra) const { + template void execute(pybind11::class_ &class_, const Extra&... extra) const { typedef typename std::conditional::value, Base, L>::type L_type; typedef typename std::conditional::value, Base, R>::type R_type; typedef op_impl op; - class_.def(op::name(), &op::execute, std::forward(extra)...); + class_.def(op::name(), &op::execute, extra...); } - template void execute_cast(pybind11::class_ &class_, Extra&&... extra) const { + template void execute_cast(pybind11::class_ &class_, const Extra&... extra) const { typedef typename std::conditional::value, Base, L>::type L_type; typedef typename std::conditional::value, Base, R>::type R_type; typedef op_impl op; - class_.def(op::name(), &op::execute_cast, std::forward(extra)...); + class_.def(op::name(), &op::execute_cast, extra...); } }; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index df43217ae..9fecaa98e 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1,5 +1,6 @@ /* - pybind11/pybind11.h: Main header file of the C++11 python binding generator library + pybind11/pybind11.h: Main header file of the C++11 python + binding generator library Copyright (c) 2015 Wenzel Jakob @@ -10,131 +11,26 @@ #pragma once #if defined(_MSC_VER) -#pragma warning(push) -#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant -#pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) -#pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name -#pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter -#pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted +# pragma warning(push) +# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant +# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning) +# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name +# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter +# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted #elif defined(__GNUG__) and !defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-but-set-parameter" -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-but-set-parameter" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif -#include "cast.h" +#include "attr.h" NAMESPACE_BEGIN(pybind11) -template struct arg_t; - -/// Annotation for keyword arguments -struct arg { - arg(const char *name) : name(name) { } - template arg_t operator=(const T &value); - const char *name; -}; - -/// Annotation for keyword arguments with default values -template struct arg_t : public arg { - arg_t(const char *name, const T &value, const char *descr = nullptr) - : arg(name), value(value), descr(descr) { } - T value; - const char *descr; -}; - -template arg_t arg::operator=(const T &value) { return arg_t(name, value); } - -/// Annotation for methods -struct is_method { PyObject *class_; is_method(object *o) : class_(o->ptr()) { } }; - -/// Annotation for documentation -struct doc { const char *value; doc(const char *value) : value(value) { } }; - -/// Annotation for function names -struct name { const char *value; name(const char *value) : value(value) { } }; - -/// Annotation indicating that a function is an overload associated with a given "sibling" -struct sibling { PyObject *value; sibling(handle value) : value(value.ptr()) { } }; - -/// Keep patient alive while nurse lives -template struct keep_alive { }; - -NAMESPACE_BEGIN(detail) - -/// Partial template helper to invoke function call policies (e.g. keep_alive) when a function is called -template struct process_dynamic; - -/// Default implementation: do nothing -template struct process_dynamic { - static void precall(PyObject *) { } - static void postcall(PyObject *, PyObject *) { } -}; - -/// Recursively iterate over variadic template arguments -template struct process_dynamic { - static void precall(PyObject *arg) { - process_dynamic::precall(arg); - process_dynamic::precall(arg); - } - static void postcall(PyObject *arg, PyObject *ret) { - process_dynamic::postcall(arg, ret); - process_dynamic::postcall(arg, ret); - } -}; - -template <> struct process_dynamic<> : public process_dynamic { }; - -NAMESPACE_END(detail) - /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object class cpp_function : public function { protected: - /// Special data structure which holds metadata about a bound function (signature, overloads, etc.) - struct function_entry { - /// Function name - char *name = nullptr; /* why no C++ strings? They generate heavier code.. */ - - // User-specified documentation string - char *doc = nullptr; - - /// Human-readable version of the function signature - char *signature = nullptr; - - /// List of registered keyword arguments - std::vector args; - - /// Pointer to lambda function which converts arguments and performs the actual call - PyObject * (*impl) (function_entry *, PyObject *, PyObject *) = nullptr; - - /// Storage for the wrapped function pointer and captured data, if any - void *data = nullptr; - - /// Pointer to custom destructor for 'data' (if needed) - void (*free_data) (void *ptr) = nullptr; - - /// Return value policy associated with this function - return_value_policy policy = return_value_policy::automatic; - - /// True if name == '__init__' - bool is_constructor = false; - - /// Python method object - PyMethodDef *def = nullptr; - - /// Pointer to class (if this is method) - PyObject *class_ = nullptr; - - /// Pointer to first registered function in overload chain - PyObject *sibling = nullptr; - - /// Pointer to next overload - function_entry *next = nullptr; - }; - - function_entry *m_entry; - /// Picks a suitable return value converter from cast.h template using return_value_caster = detail::type_caster using arg_value_caster = detail::type_caster>; - - /// Deal with annotations that can be processed at function registration time - template static void process_static(const std::tuple &args, function_entry *entry) { - process_static(args, entry, typename detail::make_index_sequence::type()); - } - - /// contd. - template static void process_static(const std::tuple &args, - function_entry *entry, detail::index_sequence) { - int unused[] = { 0, (process_static(std::get(args), entry), 0)... }; - (void) unused; - } - - /* The following overloads are used to process any annotations passed to - cpp_function. They update the corresponding fields in m_entry */ - - /// Process an annotation specifying the function's name - static void process_static(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; } - - /// Process an annotation specifying function's docstring (provided as a C-style string) - static void process_static(const char *doc, function_entry *entry) { entry->doc = (char *) doc; } - - /// Process an annotation specifying function's docstring - static void process_static(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; } - - /// Process an annotation indicating the function's return value policy - static void process_static(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; } - - /// Process an annotation which indicates that this is an overloaded function associated with a given sibling - static void process_static(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; } - - /// Process an annotation which indicates that this function is a method - static void process_static(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; } - - /// Process a keyword argument annotation (*without* a default value) - static void process_static(const pybind11::arg &a, function_entry *entry) { - if (entry->class_ && entry->args.empty()) - entry->args.emplace_back("self", nullptr, nullptr); - entry->args.emplace_back(a.name, nullptr, nullptr); - } - - /// Process a keyword argument annotation (with a default value) - template - static void process_static(const pybind11::arg_t &a, function_entry *entry) { - if (entry->class_ && entry->args.empty()) - entry->args.emplace_back("self", nullptr, nullptr); - - /* Convert keyword value into a Python object */ - PyObject *obj = detail::type_caster::type>::cast( - a.value, return_value_policy::automatic, nullptr); - - if (obj == nullptr) - 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); - } - - /// Process an annotation indicating a keep_alive call policy - template - static void process_static(const keep_alive &, function_entry *) { /* handled at call time */ } public: cpp_function() { } /// Vanilla function pointers template - cpp_function(Return (*f)(Args...), Extra&&... extra) { - using detail::descr; - m_entry = new function_entry(); - m_entry->data = (void *) f; + cpp_function(Return (*f)(Args...), const Extra&... extra) { + auto rec = new detail::function_record(); + rec->data = (void *) f; typedef arg_value_caster cast_in; typedef return_value_caster cast_out; /* Dispatch code which converts function arguments and performs the actual function call */ - m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject * { + rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle { cast_in args; /* Try to cast the function arguments into the C++ domain */ if (!args.load(pyArgs, true)) return PYBIND11_TRY_NEXT_OVERLOAD; - detail::process_dynamic::precall(pyArgs); // call policy precall + /* Invoke call policy pre-call hook */ + detail::process_attributes::precall(pyArgs); /* Do the call and convert the return value back into the Python domain */ - PyObject *result = cast_out::cast( - args.template call((Return (*) (Args...)) entry->data), - entry->policy, parent); + handle result = cast_out::cast( + args.template call((Return (*) (Args...)) rec->data), + rec->policy, parent); + + /* Invoke call policy post-call hook */ + detail::process_attributes::postcall(pyArgs, result); - detail::process_dynamic::postcall(pyArgs, result); // call policy postcall return result; }; - /* Process any user-provided function annotations */ - process_static(std::make_tuple(std::forward(extra)...), m_entry); + /* Process any user-provided function attributes */ + detail::process_attributes::init(extra..., rec); /* Generate a readable signature describing the function's arguments and return value types */ + using detail::descr; PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); /* Register the function with Python from generic (non-templated) code */ - initialize(signature.text(), signature.types(), sizeof...(Args)); + initialize(rec, signature.text(), signature.types(), sizeof...(Args)); } /// Delegating helper constructor to deal with lambda functions - template cpp_function(Func &&f, Extra&&... extra) { + template cpp_function(Func &&f, const Extra&... extra) { initialize(std::forward(f), (typename detail::remove_class::type::operator())>::type *) nullptr, - std::forward(extra)...); + &std::remove_reference::type::operator())>::type *) nullptr, extra...); } /// Delegating helper constructor to deal with class methods (non-const) template cpp_function( - Return (Class::*f)(Arg...), Extra&&... extra) { + Return (Class::*f)(Arg...), const Extra&... extra) { initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*) (Class *, Arg...)) nullptr, std::forward(extra)...); + (Return (*) (Class *, Arg...)) nullptr, extra...); } /// Delegating helper constructor to deal with class methods (const) template cpp_function( - Return (Class::*f)(Arg...) const, Extra&&... extra) { + Return (Class::*f)(Arg...) const, const Extra&... extra) { initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, - (Return (*)(const Class *, Arg ...)) nullptr, std::forward(extra)...); + (Return (*)(const Class *, Arg ...)) nullptr, extra...); } /// Return the function name - const char *name() const { return m_entry->name; } + object name() const { return attr("__name__"); } protected: /// Special internal constructor for functors, lambda functions, etc. template - void initialize(Func &&f, Return (*)(Args...), Extra&&... extra) { - using detail::descr; + void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) { struct capture { typename std::remove_reference::type f; }; /* Store the function including any extra state it might have (e.g. a lambda capture object) */ - m_entry = new function_entry(); - m_entry->data = new capture { std::forward(f) }; + auto rec = new detail::function_record(); + rec->data = new capture { std::forward(f) }; /* Create a cleanup handler, but only if we have to (less generated code) */ if (!std::is_trivially_destructible::value) - m_entry->free_data = [](void *ptr) { delete (capture *) ptr; }; + rec->free_data = [](void *ptr) { delete (capture *) ptr; }; else - m_entry->free_data = operator delete; + rec->free_data = operator delete; typedef arg_value_caster cast_in; typedef return_value_caster cast_out; /* Dispatch code which converts function arguments and performs the actual function call */ - m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject *{ + rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle { cast_in args; /* Try to cast the function arguments into the C++ domain */ if (!args.load(pyArgs, true)) return PYBIND11_TRY_NEXT_OVERLOAD; - detail::process_dynamic::precall(pyArgs); // call policy precall + /* Invoke call policy pre-call hook */ + detail::process_attributes::precall(pyArgs); /* Do the call and convert the return value back into the Python domain */ - PyObject *result = cast_out::cast( - args.template call(((capture *) entry->data)->f), - entry->policy, parent); + handle result = cast_out::cast( + args.template call(((capture *) rec->data)->f), + rec->policy, parent); + + /* Invoke call policy post-call hook */ + detail::process_attributes::postcall(pyArgs, result); - detail::process_dynamic::postcall(pyArgs, result); // call policy postcall return result; }; - /* Process any user-provided function annotations */ - process_static(std::make_tuple(std::forward(extra)...), m_entry); + /* Process any user-provided function attributes */ + detail::process_attributes::init(extra..., rec); /* Generate a readable signature describing the function's arguments and return value types */ + using detail::descr; PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); /* Register the function with Python from generic (non-templated) code */ - initialize(signature.text(), signature.types(), sizeof...(Args)); - } - - /// Main dispatch logic for calls to functions bound using pybind11 - static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs) { - /* Iterator over the list of potentially admissible overloads */ - function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr), - *it = overloads; - - /* Need to know how many arguments + keyword arguments there are to pick the right overload */ - int nargs = (int) PyTuple_Size(args), - nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0; - - PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr, - *result = PYBIND11_TRY_NEXT_OVERLOAD; - try { - for (; it != nullptr; it = it->next) { - object args_(args, true); - int kwargs_consumed = 0; - - /* For each overload: - 1. If the required list of arguments is longer than the - actually provided amount, create a copy of the argument - list and fill in any available keyword/default arguments. - 2. Ensure that all keyword arguments were "consumed" - 3. Call the function call dispatcher (function_entry::impl) - */ - - if (nargs < (int) it->args.size()) { - args_ = object(PyTuple_New(it->args.size()), false); - for (int i = 0; i < nargs; ++i) { - PyObject *item = PyTuple_GET_ITEM(args, i); - Py_INCREF(item); - PyTuple_SET_ITEM(args_.ptr(), i, item); - } - int arg_ctr = 0; - for (auto const &it2 : it->args) { - int index = arg_ctr++; - if (PyTuple_GET_ITEM(args_.ptr(), index)) - continue; - PyObject *value = nullptr; - if (kwargs) - value = PyDict_GetItemString(kwargs, it2.name); - if (value) - kwargs_consumed++; - else if (it2.value) - value = it2.value; - if (value) { - Py_INCREF(value); - PyTuple_SET_ITEM(args_.ptr(), index, value); - } else { - kwargs_consumed = -1; /* definite failure */ - break; - } - } - } - - if (kwargs_consumed == nkwargs) - result = it->impl(it, args_.ptr(), parent); - - if (result != PYBIND11_TRY_NEXT_OVERLOAD) - break; - } - } catch (const error_already_set &) { return nullptr; - } catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr; - } catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr; - } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return nullptr; - } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; - } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; - } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; - } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr; - } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; - } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return nullptr; - } catch (...) { - PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); - return nullptr; - } - - if (result == PYBIND11_TRY_NEXT_OVERLOAD) { - std::string msg = "Incompatible function arguments. The " - "following argument types are supported:\n"; - int ctr = 0; - for (function_entry *it2 = overloads; it2 != nullptr; it2 = it2->next) { - msg += " "+ std::to_string(++ctr) + ". "; - msg += it2->signature; - msg += "\n"; - } - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else if (result == nullptr) { - std::string msg = "Unable to convert function return value to a " - "Python type! The signature was\n\t"; - msg += it->signature; - PyErr_SetString(PyExc_TypeError, msg.c_str()); - return nullptr; - } else { - if (overloads->is_constructor) { - /* When a construtor ran successfully, the corresponding - holder type (e.g. std::unique_ptr) must still be initialized. */ - PyObject *inst = PyTuple_GetItem(args, 0); - auto tinfo = detail::get_type_info(Py_TYPE(inst)); - tinfo->init_holder(inst, nullptr); - } - return result; - } - } - - /// When a cpp_function is GCed, release any memory allocated by pybind11 - static void destruct(function_entry *entry) { - while (entry) { - function_entry *next = entry->next; - if (entry->free_data) - entry->free_data(entry->data); - std::free((char *) entry->name); - std::free((char *) entry->doc); - std::free((char *) entry->signature); - for (auto &arg: entry->args) { - std::free((char *) arg.name); - std::free((char *) arg.descr); - Py_XDECREF(arg.value); - } - if (entry->def) { - std::free((char *) entry->def->ml_doc); - delete entry->def; - } - delete entry; - entry = next; - } + initialize(rec, signature.text(), signature.types(), sizeof...(Args)); } /// Register a function call with Python (generic non-templated code goes here) - void initialize(const char *text, const std::type_info * const * types, int args) { + void initialize(detail::function_record *rec, const char *text, + const std::type_info *const *types, int args) { + /* Create copies of all referenced C-style strings */ - m_entry->name = strdup(m_entry->name ? m_entry->name : ""); - if (m_entry->doc) m_entry->doc = strdup(m_entry->doc); - for (auto &a: m_entry->args) { + rec->name = strdup(rec->name ? rec->name : ""); + if (rec->doc) rec->doc = strdup(rec->doc); + for (auto &a: rec->args) { if (a.name) a.name = strdup(a.name); if (a.descr) @@ -472,17 +186,17 @@ protected: break; if (c == '{') { - if (type_depth == 1 && arg_index < m_entry->args.size()) { - signature += m_entry->args[arg_index].name; + if (type_depth == 1 && arg_index < rec->args.size()) { + signature += rec->args[arg_index].name; signature += " : "; } ++type_depth; } else if (c == '}') { --type_depth; - if (type_depth == 1 && arg_index < m_entry->args.size()) { - if (m_entry->args[arg_index].descr) { + if (type_depth == 1 && arg_index < rec->args.size()) { + if (rec->args[arg_index].descr) { signature += " = "; - signature += m_entry->args[arg_index].descr; + signature += rec->args[arg_index].descr; } arg_index++; } @@ -511,67 +225,66 @@ protected: #endif #if PY_MAJOR_VERSION < 3 - if (strcmp(m_entry->name, "__next__") == 0) { - std::free(m_entry->name); - m_entry->name = strdup("next"); + if (strcmp(rec->name, "__next__") == 0) { + std::free(rec->name); + rec->name = strdup("next"); } #endif - if (!m_entry->args.empty() && (int) m_entry->args.size() != args) + if (!rec->args.empty() && (int) rec->args.size() != args) pybind11_fail( - "cpp_function(): function \"" + std::string(m_entry->name) + "\" takes " + - std::to_string(args) + " arguments, but " + std::to_string(m_entry->args.size()) + + "cpp_function(): function \"" + std::string(rec->name) + "\" takes " + + std::to_string(args) + " arguments, but " + std::to_string(rec->args.size()) + " pybind11::arg entries were specified!"); - m_entry->is_constructor = !strcmp(m_entry->name, "__init__"); - m_entry->signature = strdup(signature.c_str()); - m_entry->args.shrink_to_fit(); + rec->is_constructor = !strcmp(rec->name, "__init__"); + rec->signature = strdup(signature.c_str()); + rec->args.shrink_to_fit(); #if PY_MAJOR_VERSION < 3 - if (m_entry->sibling && PyMethod_Check(m_entry->sibling)) - m_entry->sibling = PyMethod_GET_FUNCTION(m_entry->sibling); + if (rec->sibling && PyMethod_Check(rec->sibling.ptr())) + rec->sibling = PyMethod_GET_FUNCTION(rec->sibling.ptr()); #endif - function_entry *s_entry = nullptr, *entry = m_entry; - if (m_entry->sibling && PyCFunction_Check(m_entry->sibling)) { - capsule entry_capsule(PyCFunction_GetSelf(m_entry->sibling), true); - s_entry = (function_entry *) entry_capsule; + detail::function_record *chain = nullptr, *chain_start = rec; + if (rec->sibling && PyCFunction_Check(rec->sibling.ptr())) { + capsule rec_capsule(PyCFunction_GetSelf(rec->sibling.ptr()), true); + chain = (detail::function_record *) rec_capsule; /* Never append a method to an overload chain of a parent class; instead, hide the parent's overloads in this case */ - if (s_entry->class_ != m_entry->class_) - s_entry = nullptr; + if (chain->class_ != rec->class_) + chain = nullptr; } - if (!s_entry) { + if (!chain) { /* No existing overload was found, create a new function object */ - m_entry->def = new PyMethodDef(); - memset(m_entry->def, 0, sizeof(PyMethodDef)); - m_entry->def->ml_name = m_entry->name; - m_entry->def->ml_meth = reinterpret_cast(*dispatcher); - m_entry->def->ml_flags = METH_VARARGS | METH_KEYWORDS; - capsule entry_capsule(m_entry, [](PyObject *o) { - destruct((function_entry *) PyCapsule_GetPointer(o, nullptr)); + rec->def = new PyMethodDef(); + memset(rec->def, 0, sizeof(PyMethodDef)); + rec->def->ml_name = rec->name; + rec->def->ml_meth = reinterpret_cast(*dispatcher); + rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS; + capsule rec_capsule(rec, [](PyObject *o) { + destruct((detail::function_record *) PyCapsule_GetPointer(o, nullptr)); }); - m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr()); + m_ptr = PyCFunction_New(rec->def, rec_capsule.ptr()); if (!m_ptr) 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; + m_ptr = rec->sibling.ptr(); inc_ref(); - entry = s_entry; - while (s_entry->next) - s_entry = s_entry->next; - s_entry->next = m_entry; + chain_start = chain; + while (chain->next) + chain = chain->next; + chain->next = rec; } std::string signatures; int index = 0; - function_entry *it = entry; - /* Create a nice pydoc entry including all signatures and + /* Create a nice pydoc rec including all signatures and docstrings of the functions in the overload chain */ - while (it) { - if (s_entry) + for (auto it = chain_start; it != nullptr; it = it->next) { + if (chain) signatures += std::to_string(++index) + ". "; signatures += "Signature : "; signatures += it->signature; @@ -583,23 +296,150 @@ protected: } if (it->next) signatures += "\n"; - it = it->next; } + + /* Install docstring */ PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; if (func->m_ml->ml_doc) std::free((char *) func->m_ml->ml_doc); func->m_ml->ml_doc = strdup(signatures.c_str()); - if (entry->class_) { -#if PY_MAJOR_VERSION >= 3 - m_ptr = PyInstanceMethod_New(m_ptr); -#else - m_ptr = PyMethod_New(m_ptr, nullptr, entry->class_); -#endif + + if (rec->class_) { + m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->class_.ptr()); if (!m_ptr) pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object"); Py_DECREF(func); } } + + /// When a cpp_function is GCed, release any memory allocated by pybind11 + static void destruct(detail::function_record *rec) { + while (rec) { + detail::function_record *next = rec->next; + if (rec->free_data) + rec->free_data(rec->data); + std::free((char *) rec->name); + std::free((char *) rec->doc); + std::free((char *) rec->signature); + for (auto &arg: rec->args) { + std::free((char *) arg.name); + std::free((char *) arg.descr); + arg.value.dec_ref(); + } + if (rec->def) { + std::free((char *) rec->def->ml_doc); + delete rec->def; + } + delete rec; + rec = next; + } + } + + /// Main dispatch logic for calls to functions bound using pybind11 + static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs) { + /* Iterator over the list of potentially admissible overloads */ + detail::function_record *overloads = (detail::function_record *) PyCapsule_GetPointer(self, nullptr), + *it = overloads; + + /* Need to know how many arguments + keyword arguments there are to pick the right overload */ + int nargs = (int) PyTuple_Size(args), + nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0; + + handle parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr, + result = PYBIND11_TRY_NEXT_OVERLOAD; + try { + for (; it != nullptr; it = it->next) { + tuple args_(args, true); + int kwargs_consumed = 0; + + /* For each overload: + 1. If the required list of arguments is longer than the + actually provided amount, create a copy of the argument + list and fill in any available keyword/default arguments. + 2. Ensure that all keyword arguments were "consumed" + 3. Call the function call dispatcher (function_record::impl) + */ + + if (nargs < (int) it->args.size()) { + args_ = tuple(it->args.size()); + for (int i = 0; i < nargs; ++i) { + handle item = PyTuple_GET_ITEM(args, i); + PyTuple_SET_ITEM(args_.ptr(), i, item.inc_ref().ptr()); + } + + int arg_ctr = 0; + for (auto const &it2 : it->args) { + int index = arg_ctr++; + if (PyTuple_GET_ITEM(args_.ptr(), index)) + continue; + + handle value; + if (kwargs) + value = PyDict_GetItemString(kwargs, it2.name); + + if (value) + kwargs_consumed++; + else if (it2.value) + value = it2.value; + + if (value) { + PyTuple_SET_ITEM(args_.ptr(), index, value.inc_ref().ptr()); + } else { + kwargs_consumed = -1; /* definite failure */ + break; + } + } + } + + if (kwargs_consumed == nkwargs) + result = it->impl(it, args_, parent); + + if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD) + break; + } + } catch (const error_already_set &) { return nullptr; + } catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr; + } catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr; + } catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return nullptr; + } catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; + } catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; + } catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; + } catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr; + } catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr; + } catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return nullptr; + } catch (...) { + PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); + return nullptr; + } + + if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) { + std::string msg = "Incompatible function arguments. The " + "following argument types are supported:\n"; + int ctr = 0; + for (detail::function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) { + msg += " "+ std::to_string(++ctr) + ". "; + msg += it2->signature; + msg += "\n"; + } + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else if (!result) { + std::string msg = "Unable to convert function return value to a " + "Python type! The signature was\n\t"; + msg += it->signature; + PyErr_SetString(PyExc_TypeError, msg.c_str()); + return nullptr; + } else { + if (overloads->is_constructor) { + /* When a construtor ran successfully, the corresponding + holder type (e.g. std::unique_ptr) must still be initialized. */ + PyObject *inst = PyTuple_GetItem(args, 0); + auto tinfo = detail::get_type_info(Py_TYPE(inst)); + tinfo->init_holder(inst, nullptr); + } + return result.ptr(); + } + } }; /// Wrapper for Python extension modules @@ -625,11 +465,11 @@ public: } template - module &def(const char *name_, Func &&f, Extra&& ... extra) { + module &def(const char *name_, Func &&f, const Extra& ... extra) { cpp_function func(std::forward(f), name(name_), - sibling((handle) attr(name_)), std::forward(extra)...); - func.inc_ref(); /* The following line steals a reference to 'func' */ - PyModule_AddObject(ptr(), name_, func.ptr()); + sibling((handle) attr(name_)), extra...); + /* PyModule_AddObject steals a reference to 'func' */ + PyModule_AddObject(ptr(), name_, func.inc_ref().ptr()); return *this; } @@ -652,18 +492,27 @@ public: }; NAMESPACE_BEGIN(detail) -/// Basic support for creating new Python heap types +/// Generic support for creating new Python heap types class generic_type : public object { + template friend class class_; public: PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check) - - generic_type(const object &scope, const char *name_, const std::type_info *type_cpp, - size_t type_size, size_t instance_size, - void (*init_holder)(PyObject *, const void *), - const destructor &dealloc, object parent, const char *doc) { +protected: + void initialize(type_record *rec) { + if (rec->base_type) { + if (rec->base_handle) + pybind11_fail("generic_type: specified base type multiple times!"); + rec->base_handle = detail::get_type_handle(*(rec->base_type)); + if (!rec->base_handle) { + std::string tname(rec->base_type->name()); + detail::clean_type_id(tname); + pybind11_fail("generic_type: type \"" + std::string(rec->name) + + "\" referenced unknown base type \"" + tname + "\""); + } + } object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false); - object name(PYBIND11_FROM_STRING(name_), false); + object name(PYBIND11_FROM_STRING(rec->name), false); auto type = (PyHeapTypeObject*) type_holder.ptr(); if (!type_holder || !name) @@ -673,25 +522,26 @@ public: auto &internals = get_internals(); detail::type_info *tinfo = new detail::type_info(); tinfo->type = (PyTypeObject *) type; - tinfo->type_size = type_size; - tinfo->init_holder = init_holder; - internals.registered_types_cpp[type_cpp] = tinfo; + tinfo->type_size = rec->type_size; + tinfo->init_holder = rec->init_holder; + internals.registered_types_cpp[rec->type] = tinfo; internals.registered_types_py[type] = tinfo; - auto scope_module = (object) scope.attr("__module__"); + auto scope_module = (object) rec->scope.attr("__module__"); if (!scope_module) - scope_module = (object) scope.attr("__name__"); + scope_module = (object) rec->scope.attr("__name__"); - std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + name_) - : std::string(name_)); + std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name) + : std::string(rec->name)); /* Basic type attributes */ type->ht_type.tp_name = strdup(full_name.c_str()); - type->ht_type.tp_basicsize = instance_size; - type->ht_type.tp_base = (PyTypeObject *) parent.release(); + type->ht_type.tp_basicsize = rec->instance_size; + type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr(); + rec->base_handle.inc_ref(); #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 /* Qualified names for Python >= 3.3 */ - auto scope_qualname = (object) scope.attr("__qualname__"); + auto scope_qualname = (object) rec->scope.attr("__qualname__"); if (scope_qualname) { type->ht_qualname = PyUnicode_FromFormat( "%U.%U", scope_qualname.ptr(), name.ptr()); @@ -700,7 +550,7 @@ public: name.inc_ref(); } #endif - type->ht_name = name.release(); + type->ht_name = name.release().ptr(); /* Supported protocols */ type->ht_type.tp_as_number = &type->as_number; @@ -710,7 +560,7 @@ public: /* Supported elementary operations */ type->ht_type.tp_init = (initproc) init; type->ht_type.tp_new = (newfunc) new_instance; - type->ht_type.tp_dealloc = dealloc; + type->ht_type.tp_dealloc = rec->dealloc; /* Support weak references (needed for the keep_alive feature) */ type->ht_type.tp_weaklistoffset = offsetof(instance, weakrefs); @@ -722,12 +572,12 @@ public: #endif type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; - if (doc) { + if (rec->doc) { /* Allocate memory for docstring (using PyObject_MALLOC, since Python will free this later on) */ - size_t size = strlen(doc) + 1; + size_t size = strlen(rec->doc) + 1; type->ht_type.tp_doc = (char *) PyObject_MALLOC(size); - memcpy((void *) type->ht_type.tp_doc, doc, size); + memcpy((void *) type->ht_type.tp_doc, rec->doc, size); } if (PyType_Ready(&type->ht_type) < 0) @@ -736,15 +586,14 @@ public: m_ptr = type_holder.ptr(); if (scope_module) // Needed by pydoc - type_holder.attr("__module__") = scope_module; + attr("__module__") = scope_module; /* Register type with the parent scope */ - scope.attr(name_) = *this; + rec->scope.attr(handle(type->ht_name)) = *this; type_holder.release(); } -protected: /// Allocate a metaclass on demand (for static properties) handle metaclass() { auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type; @@ -758,17 +607,21 @@ protected: pybind11_fail("generic_type::metaclass(): unable to create type object!"); auto type = (PyHeapTypeObject*) type_holder.ptr(); - type->ht_name = name.release(); - + type->ht_name = name.release().ptr(); +#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3 + /* Qualified names for Python >= 3.3 */ + type->ht_qualname = PyUnicode_FromFormat( + "%U__Meta", ((object) attr("__qualname__")).ptr()); +#endif type->ht_type.tp_name = strdup(name_.c_str()); - type->ht_type.tp_base = &PyType_Type; + type->ht_type.tp_base = ob_type; type->ht_type.tp_flags |= (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE) & ~Py_TPFLAGS_HAVE_GC; if (PyType_Ready(&type->ht_type) < 0) pybind11_fail("generic_type::metaclass(): PyType_Ready failed!"); - ob_type = (PyTypeObject *) type_holder.release(); + ob_type = (PyTypeObject *) type_holder.release().ptr(); } return handle((PyObject *) ob_type); } @@ -851,65 +704,64 @@ protected: static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; } }; - -/* Forward declarations */ -enum op_id : int; -enum op_type : int; -struct undefined_t; -template struct op_; -template struct init; NAMESPACE_END(detail) -template > class class_ : public detail::generic_type { +template > +class class_ : public detail::generic_type { public: typedef detail::instance instance_type; PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check) - class_(object &scope, const char *name, const char *doc = nullptr) - : detail::generic_type(scope, name, &typeid(type), sizeof(type), - sizeof(instance_type), init_holder, dealloc, - object(), doc) { } + template + class_(handle scope, const char *name, const Extra &... extra) { + detail::type_record record; + record.scope = scope; + record.name = name; + record.type = &typeid(type); + record.type_size = sizeof(type); + record.instance_size = sizeof(instance_type); + record.init_holder = init_holder; + record.dealloc = dealloc; - class_(object &scope, const char *name, object &parent, - const char *doc = nullptr) - : detail::generic_type(scope, name, &typeid(type), sizeof(type), - sizeof(instance_type), init_holder, dealloc, - parent, doc) { } + /* Process optional arguments, if any */ + detail::process_attributes::init(extra..., &record); + + detail::generic_type::initialize(&record); + } template - class_ &def(const char *name_, Func&& f, Extra&&... extra) { + class_ &def(const char *name_, Func&& f, const Extra&... extra) { cpp_function cf(std::forward(f), name(name_), - sibling(attr(name_)), is_method(this), - std::forward(extra)...); + sibling(attr(name_)), is_method(*this), + extra...); attr(cf.name()) = cf; return *this; } template class_ & - def_static(const char *name_, Func f, Extra&&... extra) { + def_static(const char *name_, Func f, const Extra&... extra) { cpp_function cf(std::forward(f), name(name_), - sibling(attr(name_)), - std::forward(extra)...); + sibling(attr(name_)), extra...); attr(cf.name()) = cf; return *this; } template - class_ &def(const detail::op_ &op, Extra&&... extra) { - op.template execute(*this, std::forward(extra)...); + class_ &def(const detail::op_ &op, const Extra&... extra) { + op.template execute(*this, extra...); return *this; } template - class_ & def_cast(const detail::op_ &op, Extra&&... extra) { - op.template execute_cast(*this, std::forward(extra)...); + class_ & def_cast(const detail::op_ &op, const Extra&... extra) { + op.template execute_cast(*this, extra...); return *this; } template - class_ &def(const detail::init &init, Extra&&... extra) { - init.template execute(*this, std::forward(extra)...); + class_ &def(const detail::init &init, const Extra&... extra) { + init.template execute(*this, extra...); return *this; } @@ -926,28 +778,28 @@ public: } template - class_ &def_readwrite(const char *name, D C::*pm, Extra&&... extra) { + class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, return_value_policy::reference_internal, - is_method(this), extra...), + is_method(*this), extra...), fset([pm](C &c, const D &value) { c.*pm = value; }, - is_method(this), extra...); + is_method(*this), extra...); def_property(name, fget, fset); return *this; } template - class_ &def_readonly(const char *name, const D C::*pm, Extra&& ...extra) { + class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, return_value_policy::reference_internal, - is_method(this), std::forward(extra)...); + is_method(*this), extra...); def_property_readonly(name, fget); return *this; } template - class_ &def_readwrite_static(const char *name, D *pm, Extra&& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr, + class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, return_value_policy::reference_internal, extra...), fset([pm](object, const D &value) { *pm = value; }, extra...); def_property_static(name, fget, fset); @@ -955,9 +807,9 @@ public: } template - class_ &def_readonly_static(const char *name, const D *pm, Extra&& ...extra) { - cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr, - return_value_policy::reference_internal, std::forward(extra)...); + class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) { + cpp_function fget([pm](object) -> const D &{ return *pm; }, + return_value_policy::reference_internal, extra...); def_property_readonly_static(name, fget); return *this; } @@ -973,21 +825,19 @@ public: } class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) { - object doc_obj = doc ? pybind11::str(doc) : (object) const_cast(fget).attr("__doc__"); + object doc_obj = doc ? pybind11::str(doc) : (object) fget.attr("__doc__"); object property( - PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, - fget.ptr() ? fget.ptr() : Py_None, - fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false); + PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None, + fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false); attr(name) = property; return *this; } class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) { - object doc_obj = doc ? pybind11::str(doc) : (object) const_cast(fget).attr("__doc__"); + object doc_obj = doc ? pybind11::str(doc) : (object) fget.attr("__doc__"); object property( - PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, - fget.ptr() ? fget.ptr() : Py_None, - fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false); + PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None, + fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false); metaclass().attr(name) = property; return *this; } @@ -1047,8 +897,9 @@ private: /// Binds C++ enumerations and enumeration classes to Python template class enum_ : public class_ { public: - enum_(object &scope, const char *name, const char *doc = nullptr) - : class_(scope, name, doc), m_parent(scope) { + template + enum_(const handle &scope, const char *name, const Extra&... extra) + : class_(scope, name, extra...), m_parent(scope) { auto entries = new std::unordered_map(); this->def("__repr__", [name, entries](Type value) -> std::string { auto it = entries->find((int) value); @@ -1078,21 +929,21 @@ public: } private: std::unordered_map *m_entries; - object &m_parent; + handle m_parent; }; NAMESPACE_BEGIN(detail) template struct init { - template void execute(pybind11::class_ &class_, Extra&&... extra) const { + template void execute(pybind11::class_ &class_, const Extra&... extra) const { /// Function which calls a specific C++ in-place constructor - class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, std::forward(extra)...); + class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, extra...); } }; -PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *arg, PyObject *ret) { +PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, handle args, handle ret) { /* Clever approach based on weak references taken from Boost.Python */ - handle nurse (Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret); - handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret); + handle nurse (Nurse > 0 ? PyTuple_GetItem(args.ptr(), Nurse - 1) : ret.ptr()); + handle patient(Patient > 0 ? PyTuple_GetItem(args.ptr(), Patient - 1) : ret.ptr()); if (!nurse || !patient) pybind11_fail("Could not activate keep_alive!"); @@ -1106,17 +957,6 @@ PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject * (void) wr.release(); } -template struct process_dynamic> : public process_dynamic { - template ::type = 0> - static void precall(PyObject *arg) { keep_alive_impl(Nurse, Patient, arg, nullptr); } - template ::type = 0> - static void postcall(PyObject *, PyObject *) { } - template ::type = 0> - static void precall(PyObject *) { } - template ::type = 0> - static void postcall(PyObject *arg, PyObject *ret) { keep_alive_impl(Nurse, Patient, arg, ret); } -}; - NAMESPACE_END(detail) template detail::init init() { return detail::init(); }; @@ -1198,8 +1038,7 @@ inline function get_overload(const void *this_ptr, const char *name) { NAMESPACE_END(pybind11) #if defined(_MSC_VER) -#pragma warning(pop) +# pragma warning(pop) #elif defined(__GNUG__) and !defined(__clang__) -#pragma GCC diagnostic pop +# pragma GCC diagnostic pop #endif - diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 10488c3fe..2ce6ed229 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -30,10 +30,11 @@ public: handle(const handle &other) : m_ptr(other.m_ptr) { } handle(PyObject *ptr) : m_ptr(ptr) { } PyObject *ptr() const { return m_ptr; } - void inc_ref() const { Py_XINCREF(m_ptr); } - void dec_ref() const { Py_XDECREF(m_ptr); } + PyObject *&ptr() { return m_ptr; } + const handle& inc_ref() const { Py_XINCREF(m_ptr); return *this; } + const handle& dec_ref() const { Py_XDECREF(m_ptr); return *this; } int ref_count() const { return (int) Py_REFCNT(m_ptr); } - handle get_type() const { return (PyObject *) Py_TYPE(m_ptr); } + handle get_type() const { return handle((PyObject *) Py_TYPE(m_ptr)); } inline iterator begin() const; inline iterator end() const; inline detail::accessor operator[](handle key) const; @@ -44,6 +45,8 @@ public: template T cast() const; template object call(Args&&... args_) const; operator bool() const { return m_ptr != nullptr; } + bool operator==(const handle &h) const { return m_ptr == h.m_ptr; } + bool operator!=(const handle &h) const { return m_ptr != h.m_ptr; } bool check() const { return m_ptr != nullptr; } protected: PyObject *m_ptr; @@ -59,25 +62,25 @@ public: object(object &&other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; } ~object() { dec_ref(); } - PyObject * release() { + handle release() { PyObject *tmp = m_ptr; m_ptr = nullptr; - return tmp; + return handle(tmp); } object& operator=(object &other) { - Py_XINCREF(other.m_ptr); - Py_XDECREF(m_ptr); + other.inc_ref(); + dec_ref(); m_ptr = other.m_ptr; return *this; } object& operator=(object &&other) { if (this != &other) { - PyObject *temp = m_ptr; + handle temp(m_ptr); m_ptr = other.m_ptr; other.m_ptr = nullptr; - Py_XDECREF(temp); + temp.dec_ref(); } return *this; } @@ -85,7 +88,7 @@ public: class iterator : public object { public: - iterator(PyObject *obj, bool borrowed = false) : object(obj, borrowed) { ++*this; } + iterator(handle obj, bool borrowed = false) : object(obj, borrowed) { ++*this; } iterator& operator++() { if (ptr()) value = object(PyIter_Next(m_ptr), false); @@ -100,108 +103,106 @@ private: }; NAMESPACE_BEGIN(detail) -inline PyObject *get_function(PyObject *value) { - if (value == nullptr) - return nullptr; +inline handle get_function(handle value) { + if (value) { #if PY_MAJOR_VERSION >= 3 - if (PyInstanceMethod_Check(value)) - value = PyInstanceMethod_GET_FUNCTION(value); + if (PyInstanceMethod_Check(value.ptr())) + value = PyInstanceMethod_GET_FUNCTION(value.ptr()); #endif - if (PyMethod_Check(value)) - value = PyMethod_GET_FUNCTION(value); + if (PyMethod_Check(value.ptr())) + value = PyMethod_GET_FUNCTION(value.ptr()); + } return value; } class accessor { public: - accessor(PyObject *obj, PyObject *key, bool attr) - : obj(obj), key(key), attr(attr) { Py_INCREF(key); } - accessor(PyObject *obj, const char *key, bool attr) - : obj(obj), key(PyUnicode_FromString(key)), attr(attr) { } - accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr) - { Py_INCREF(key); } - ~accessor() { Py_DECREF(key); } + accessor(handle obj, handle key, bool attr) + : obj(obj), key(key, true), attr(attr) { } + accessor(handle obj, const char *key, bool attr) + : obj(obj), key(PyUnicode_FromString(key), false), attr(attr) { } + accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr) { } void operator=(accessor o) { operator=(object(o)); } - void operator=(const handle &h) { + void operator=(const handle &value) { if (attr) { - if (PyObject_SetAttr(obj, key, (PyObject *) h.ptr()) < 0) + if (PyObject_SetAttr(obj.ptr(), key.ptr(), value.ptr()) == -1) pybind11_fail("Unable to set object attribute"); } else { - if (PyObject_SetItem(obj, key, (PyObject *) h.ptr()) < 0) + if (PyObject_SetItem(obj.ptr(), key.ptr(), value.ptr()) == -1) pybind11_fail("Unable to set object item"); } } operator object() const { - object result(attr ? PyObject_GetAttr(obj, key) - : PyObject_GetItem(obj, key), false); - if (!result) PyErr_Clear(); + object result(attr ? PyObject_GetAttr(obj.ptr(), key.ptr()) + : PyObject_GetItem(obj.ptr(), key.ptr()), false); + if (!result) {PyErr_Clear(); } return result; } operator bool() const { if (attr) { - return (bool) PyObject_HasAttr(obj, key); + return (bool) PyObject_HasAttr(obj.ptr(), key.ptr()); } else { - object result(PyObject_GetItem(obj, key), false); + object result(PyObject_GetItem(obj.ptr(), key.ptr()), false); if (!result) PyErr_Clear(); return (bool) result; } }; private: - PyObject *obj; - PyObject *key; + handle obj; + object key; bool attr; }; struct list_accessor { public: - list_accessor(PyObject *list, size_t index) : list(list), index(index) { } + list_accessor(handle list, size_t index) : list(list), index(index) { } void operator=(list_accessor o) { return operator=(object(o)); } void operator=(const handle &o) { - o.inc_ref(); // PyList_SetItem steals a reference - if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0) + // PyList_SetItem steals a reference to 'o' + if (PyList_SetItem(list.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0) pybind11_fail("Unable to assign value in Python list!"); } operator object() const { - PyObject *result = PyList_GetItem(list, (ssize_t) index); + PyObject *result = PyList_GetItem(list.ptr(), (ssize_t) index); if (!result) pybind11_fail("Unable to retrieve value from Python list!"); return object(result, true); } private: - PyObject *list; + handle list; size_t index; }; struct tuple_accessor { public: - tuple_accessor(PyObject *tuple, size_t index) : tuple(tuple), index(index) { } + tuple_accessor(handle tuple, size_t index) : tuple(tuple), index(index) { } void operator=(tuple_accessor o) { return operator=(object(o)); } void operator=(const handle &o) { - o.inc_ref(); // PyTuple_SetItem steals a reference - if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0) + // PyTuple_SetItem steals a referenceto 'o' + if (PyTuple_SetItem(tuple.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0) pybind11_fail("Unable to assign value in Python tuple!"); } operator object() const { - PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index); + PyObject *result = PyTuple_GetItem(tuple.ptr(), (ssize_t) index); if (!result) pybind11_fail("Unable to retrieve value from Python tuple!"); return object(result, true); } private: - PyObject *tuple; + handle tuple; size_t index; }; struct dict_iterator { public: - dict_iterator(PyObject *dict = nullptr, ssize_t pos = -1) : dict(dict), pos(pos) { } + dict_iterator(handle dict = handle(), ssize_t pos = -1) : dict(dict), pos(pos) { } dict_iterator& operator++() { - if (!PyDict_Next(dict, &pos, &key, &value)) + if (!PyDict_Next(dict.ptr(), &pos, &key.ptr(), &value.ptr())) pos = -1; return *this; } @@ -211,7 +212,7 @@ public: bool operator==(const dict_iterator &it) const { return it.pos == pos; } bool operator!=(const dict_iterator &it) const { return it.pos != pos; } private: - PyObject *dict, *key, *value; + handle dict, key, value; ssize_t pos = 0; }; @@ -410,8 +411,8 @@ public: 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()); } + detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); } + void append(const object &object) const { PyList_Append(m_ptr, object.ptr()); } }; class set : public object { @@ -421,7 +422,7 @@ public: if (!m_ptr) pybind11_fail("Could not allocate set object!"); } size_t size() const { return (size_t) PySet_Size(m_ptr); } - bool add(const object &object) const { return PySet_Add(m_ptr, (PyObject *) object.ptr()) == 0; } + bool add(const object &object) const { return PySet_Add(m_ptr, object.ptr()) == 0; } void clear() const { PySet_Clear(m_ptr); } }; @@ -429,8 +430,8 @@ class function : public object { public: PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check) bool is_cpp_function() const { - PyObject *ptr = detail::get_function(m_ptr); - return ptr != nullptr && PyCFunction_Check(ptr); + handle fun = detail::get_function(m_ptr); + return fun && PyCFunction_Check(fun.ptr()); } }; @@ -448,57 +449,4 @@ public: } }; -NAMESPACE_BEGIN(detail) -PYBIND11_NOINLINE inline internals &get_internals() { - static internals *internals_ptr = nullptr; - if (internals_ptr) - return *internals_ptr; - handle builtins(PyEval_GetBuiltins()); - capsule caps(builtins["__pybind11__"]); - if (caps.check()) { - internals_ptr = caps; - } else { - internals_ptr = new internals(); - builtins["__pybind11__"] = capsule(internals_ptr); - } - return *internals_ptr; -} - -PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) { - auto const &type_dict = get_internals().registered_types_py; - do { - auto it = type_dict.find(type); - if (it != type_dict.end()) - return (detail::type_info *) it->second; - type = type->tp_base; - if (type == nullptr) - pybind11_fail("pybind11::detail::get_type_info: unable to find type object!"); - } while (true); -} - -PYBIND11_NOINLINE inline std::string error_string() { - std::string errorString; - PyThreadState *tstate = PyThreadState_GET(); - if (tstate == nullptr) - return ""; - - if (tstate->curexc_type) { - errorString += (std::string) handle(tstate->curexc_type).str(); - errorString += ": "; - } - if (tstate->curexc_value) - errorString += (std::string) handle(tstate->curexc_value).str(); - - return errorString; -} - -PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr) { - auto instances = get_internals().registered_instances; - auto it = instances.find(ptr); - if (it == instances.end()) - return handle(); - return handle((PyObject *) it->second); -} - -NAMESPACE_END(detail) NAMESPACE_END(pybind11) diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index c57befaf8..5153fec9c 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -26,7 +26,7 @@ template struct type_caster type; typedef type_caster value_conv; public: - bool load(PyObject *src, bool convert) { + bool load(handle src, bool convert) { list l(src, true); if (!l.check()) return false; @@ -34,21 +34,21 @@ public: value.clear(); value_conv conv; for (auto it : l) { - if (!conv.load(it.ptr(), convert)) + if (!conv.load(it, convert)) return false; value.push_back((Value) conv); } return true; } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const type &src, return_value_policy policy, handle parent) { list l(src.size()); size_t index = 0; for (auto const &value: src) { - object value_(value_conv::cast(value, policy, parent), false); + object value_ = object(value_conv::cast(value, policy, parent), false); if (!value_) - return nullptr; - PyList_SET_ITEM(l.ptr(), index++, value_.release()); // steals a reference + return handle(); + PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference } return l.release(); } @@ -59,26 +59,26 @@ template struct type_caster type; typedef type_caster key_conv; public: - bool load(PyObject *src, bool convert) { + bool load(handle src, bool convert) { pybind11::set s(src, true); if (!s.check()) return false; value.clear(); key_conv conv; for (auto entry : s) { - if (!conv.load(entry.ptr(), convert)) + if (!conv.load(entry, convert)) return false; value.insert((Key) conv); } return true; } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const type &src, return_value_policy policy, handle parent) { pybind11::set s; for (auto const &value: src) { - object value_(key_conv::cast(value, policy, parent), false); + object value_ = object(key_conv::cast(value, policy, parent), false); if (!value_ || !s.add(value)) - return nullptr; + return handle(); } return s.release(); } @@ -91,7 +91,7 @@ public: typedef type_caster key_conv; typedef type_caster value_conv; - bool load(PyObject *src, bool convert) { + bool load(handle src, bool convert) { dict d(src, true); if (!d.check()) return false; @@ -107,13 +107,13 @@ public: return true; } - static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { + static handle cast(const type &src, return_value_policy policy, handle parent) { 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); + object key = object(key_conv::cast(kv.first, policy, parent), false); + object value = object(value_conv::cast(kv.second, policy, parent), false); if (!key || !value) - return nullptr; + return handle(); d[key] = value; } return d.release(); diff --git a/setup.py b/setup.py index 691a51a53..2f02e6a39 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,7 @@ setup( packages=[], license='BSD', headers=[ + 'include/pybind11/attr.h', 'include/pybind11/cast.h', 'include/pybind11/complex.h', 'include/pybind11/descr.h', @@ -58,10 +59,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 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.""")