general cleanup of the 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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ebb4ea6..4160719 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,6 +80,7 @@
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 @@
# 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 4c2b23b..4683c70 100644
--- a/docs/advanced.rst
+++ b/docs/advanced.rst
@@ -925,7 +925,7 @@
FUNCTIONS
...
| myFunction(...)
- | Signature : (MyClass, arg : SomeType = <SomeType object at 0x101b7b080>) -> None
+ | Signature : (MyClass, arg : SomeType = <SomeType object at 0x101b7b080>) -> NoneType
...
The first way of addressing this is by defining ``SomeType.__repr__``.
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b992303..d497e26 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -5,15 +5,19 @@
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 @@
* 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 5aad4d1..cfe349f 100644
--- a/docs/classes.rst
+++ b/docs/classes.rst
@@ -177,9 +177,22 @@
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_<Pet>(m, "Pet")
+ .def(py::init<const std::string &>())
+ .def_readwrite("name", &Pet::name);
+
+ py::class_<Dog>(m, "Dog", py::base<Pet>() /* <- specify C++ parent type */)
+ .def(py::init<const std::string &>())
+ .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 @@
pet.def(py::init<const std::string &>())
.def_readwrite("name", &Pet::name);
- py::class_<Dog>(m, "Dog", pet /* <- specify parent */)
+ py::class_<Dog>(m, "Dog", pet /* <- specify Python parent type */)
.def(py::init<const std::string &>())
.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 @@
| 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 50d50b5..6172859 100644
--- a/docs/cmake.rst
+++ b/docs/cmake.rst
@@ -30,10 +30,14 @@
# Try to autodetect Python (can be overridden manually if needed)
set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7)
if (NOT ${PYBIND11_PYTHON_VERSION} STREQUAL "")
- find_package(PythonLibs ${PYBIND11_PYTHON_VERSION} EXACT REQUIRED)
+ find_package(PythonLibs ${PYBIND11_PYTHON_VERSION} EXACT)
+ if (NOT PythonLibs_FOUND)
+ find_package(PythonLibs ${PYBIND11_PYTHON_VERSION} REQUIRED)
+ endif()
else()
find_package(PythonLibs REQUIRED)
endif()
+ find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED)
# Uncomment the following line if you will also require a matching Python interpreter
# find_package(PythonInterp ${PYTHONLIBS_VERSION_STRING} EXACT REQUIRED)
@@ -115,7 +119,7 @@
# 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 ab5c11b..4df4344 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -63,17 +63,19 @@
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 @@
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 cabd847..ad24ef0 100644
--- a/example/example5.cpp
+++ b/example/example5.cpp
@@ -29,6 +29,11 @@
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 @@
.def("name", &Pet::name)
.def("species", &Pet::species);
+ /* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>());
+ /* Another way of declaring a subclass relationship: reference parent's C++ type */
+ py::class_<Rabbit>(m, "Rabbit", py::base<Pet>())
+ .def(py::init<std::string>());
+
m.def("pet_print", pet_print);
m.def("dog_bark", dog_bark);
diff --git a/example/example5.py b/example/example5.py
index b7e3cd3..0699978 100755
--- a/example/example5.py
+++ b/example/example5.py
@@ -5,11 +5,15 @@
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 41544c8..d1da883 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 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 0000000..9a646cb
--- /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 <wenzel@inf.ethz.ch>
+
+ 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 <typename T> struct arg_t;
+
+/// Annotation for keyword arguments
+struct arg {
+ arg(const char *name) : name(name) { }
+ template <typename T> arg_t<T> operator=(const T &value);
+ const char *name;
+};
+
+/// Annotation for keyword arguments with default values
+template <typename T> 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 <typename T> arg_t<T> arg::operator=(const T &value) { return arg_t<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 <typename T> struct base { };
+
+/// Keep patient alive while nurse lives
+template <int Nurse, int Patient> struct keep_alive { };
+
+NAMESPACE_BEGIN(detail)
+/* Forward declarations */
+enum op_id : int;
+enum op_type : int;
+struct undefined_t;
+template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
+template <typename... Args> 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<argument_record> 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<type>?
+ 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 <typename T, typename SFINAE = void> struct process_attribute;
+
+template <typename T> 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<name> : process_attribute_default<name> {
+ static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
+};
+
+/// Process an attribute specifying the function's docstring
+template <> struct process_attribute<doc> : process_attribute_default<doc> {
+ static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
+};
+
+/// Process an attribute specifying the function's docstring (provided as a C-style string)
+template <> struct process_attribute<const char *> : process_attribute_default<const char *> {
+ static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
+ static void init(const char *d, type_record *r) { r->doc = const_cast<char *>(d); }
+};
+template <> struct process_attribute<char *> : process_attribute<const char *> { };
+
+/// Process an attribute indicating the function's return value policy
+template <> struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
+ 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<sibling> : process_attribute_default<sibling> {
+ 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<is_method> : process_attribute_default<is_method> {
+ 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<arg> : process_attribute_default<arg> {
+ 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 <typename T>
+struct process_attribute<arg_t<T>> : process_attribute_default<arg_t<T>> {
+ static void init(const arg_t<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<typename detail::intrinsic_type<T>::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 <typename T>
+struct process_attribute<T, typename std::enable_if<std::is_base_of<handle, T>::value>::type> : process_attribute_default<handle> {
+ static void init(const handle &h, type_record *r) { r->base_handle = h; }
+};
+
+/// Process a parent class attribute
+template <typename T>
+struct process_attribute<base<T>> : process_attribute_default<base<T>> {
+ static void init(const base<T> &, 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 <int Nurse, int Patient> struct process_attribute<keep_alive<Nurse, Patient>> : public process_attribute_default<keep_alive<Nurse, Patient>> {
+ template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
+ static void precall(handle args) { keep_alive_impl(Nurse, Patient, args, handle()); }
+ template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
+ static void postcall(handle, handle) { }
+ template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
+ static void precall(handle) { }
+ template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
+ static void postcall(handle args, handle ret) { keep_alive_impl(Nurse, Patient, args, ret); }
+};
+
+
+/// Recursively iterate over variadic template arguments
+template <typename... Args> struct process_attributes {
+ static void init(const Args&... args, function_record *r) {
+ int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
+ (void) unused;
+ }
+ static void init(const Args&... args, type_record *r) {
+ int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::init(args, r), 0) ... };
+ (void) unused;
+ }
+ static void precall(handle fn_args) {
+ int unused[] = { 0, (process_attribute<typename std::decay<Args>::type>::precall(fn_args), 0) ... };
+ (void) unused;
+ }
+ static void postcall(handle fn_args, handle fn_ret) {
+ int unused[] = { 0, (process_attribute<typename std::decay<Args>::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 8fab99d..bc26e47 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<PyObject *(*)(PyObject *, PyTypeObject *) > 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<void> *) src)->value;
+ if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
+ value = ((instance<void> *) 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<void *>(_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<void> *) parent)->value == (void *) src;
+ parent && ((instance<void> *) 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<void> *inst = (instance<void> *) 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<void> *) 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 @@
public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); }
- 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 @@
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 @@
typedef typename std::conditional<std::is_floating_point<T>::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<T>::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<T>::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<T>::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 @@
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<T>::value) {
return PyFloat_FromDouble((double) src);
} else if (sizeof(T) <= sizeof(long)) {
@@ -216,12 +289,13 @@
}
}
- 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 <typename T2 = T, typename std::enable_if<std::is_integral<T2>::value, int>::type = 0>
static PYBIND11_DESCR name() { return type_descr(_("int")); }
+
template <typename T2 = T, typename std::enable_if<!std::is_integral<T2>::value, int>::type = 0>
static PYBIND11_DESCR name() { return type_descr(_("float")); }
@@ -234,10 +308,9 @@
template <> class type_caster<void_type> {
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<bool> {
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<std::string> {
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 @@
template <> class type_caster<char> {
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 @@
template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
typedef std::pair<T1, T2> 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<typename intrinsic_type<T1>::type>::cast(src.first, policy, parent), false);
- object o2(type_caster<typename intrinsic_type<T2>::type>::cast(src.second, policy, parent), false);
+ static handle cast(const type &src, return_value_policy policy, handle parent) {
+ object o1 = object(type_caster<typename intrinsic_type<T1>::type>::cast(src.first, policy, parent), false);
+ object o2 = object(type_caster<typename intrinsic_type<T2>::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 @@
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<sizeof...(Tuple)>::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<size>::type());
}
@@ -398,36 +466,32 @@
return type((Tuple) std::get<Index>(value)...);
}
- template <size_t ... Indices> bool load(PyObject *src, bool convert, index_sequence<Indices...>) {
- if (!PyTuple_Check(src))
+ template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) {
+ if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != size)
return false;
- if (PyTuple_Size(src) != size)
- return false;
- std::array<bool, size> results {{
- (PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get<Indices>(value).load(PyTuple_GET_ITEM(src, Indices), convert) : false)...
+ std::array<bool, size> success {{
+ (PyTuple_GET_ITEM(src.ptr(), Indices) != nullptr ? std::get<Indices>(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 <size_t ... Indices> static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent, index_sequence<Indices...>) {
- std::array<object, size> results {{
+ template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) {
+ std::array<object, size> entries {{
object(type_caster<typename intrinsic_type<Tuple>::type>::cast(std::get<Indices>(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 @@
using type_caster<type>::temp;
using type_caster<type>::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<type, holder_type> *) src;
+
+ if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
+ auto inst = (instance<type, holder_type> *) 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 @@
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 @@
struct type_caster<type, typename std::enable_if<std::is_base_of<handle, type>::value>::type> {
public:
template <typename T = type, typename std::enable_if<std::is_same<T, handle>::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 <typename T = type, typename std::enable_if<!std::is_same<T, handle>::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<type>::name());
};
NAMESPACE_END(detail)
-template <typename T> inline T cast(PyObject *object) {
+template <typename T> inline T cast(handle handle) {
detail::type_caster<typename detail::intrinsic_type<T>::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 <typename T> inline object cast(const T &value, return_value_policy policy = return_value_policy::automatic, PyObject *parent = nullptr) {
+template <typename T> 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<T>::value ? return_value_policy::take_ownership : return_value_policy::copy;
return object(detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(value, policy, parent), false);
@@ -514,23 +580,22 @@
template <typename... Args> inline object handle::call(Args&&... args_) const {
const size_t size = sizeof...(Args);
- std::array<object, size> args{
+ std::array<object, size> args {
{ object(detail::type_caster<typename detail::intrinsic_type<Args>::type>::cast(
std::forward<Args>(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 ff84661..a0af678 100644
--- a/include/pybind11/common.h
+++ b/include/pybind11/common.h
@@ -73,6 +73,7 @@
#include <memory>
#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 @@
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<PyObject *(*)(PyObject *, PyTypeObject *)> 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<const PyObject *, const char *>& v) const {
size_t value = std::hash<const void *>()(v.first);
@@ -218,16 +210,6 @@
}
};
-/// 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<const void *, void*> registered_types_cpp; // std::type_info* -> type_info
diff --git a/include/pybind11/complex.h b/include/pybind11/complex.h
index be0614c..10da21d 100644
--- a/include/pybind11/complex.h
+++ b/include/pybind11/complex.h
@@ -20,8 +20,8 @@
NAMESPACE_BEGIN(detail)
template <typename T> class type_caster<std::complex<T>> {
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 @@
return true;
}
- static PyObject *cast(const std::complex<T> &src, return_value_policy /* policy */, PyObject * /* parent */) {
+ static handle cast(const std::complex<T> &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 5bc8988..95ac7c9 100644
--- a/include/pybind11/functional.h
+++ b/include/pybind11/functional.h
@@ -18,14 +18,13 @@
template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> {
typedef std::function<Return(Args...)> 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<Return>());
};
@@ -33,10 +32,8 @@
}
template <typename Func>
- static PyObject *cast(Func &&f_, return_value_policy policy, PyObject *) {
- cpp_function f(std::forward<Func>(f_), policy);
- f.inc_ref();
- return f.ptr();
+ static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
+ return cpp_function(std::forward<Func>(f_), policy).release();
}
PYBIND11_TYPE_CASTER(type, _("function<") +
diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h
index 2365a60..0aaac96 100644
--- a/include/pybind11/numpy.h
+++ b/include/pybind11/numpy.h
@@ -79,36 +79,36 @@
API& api = lookup_api();
PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<Type>::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 @@
/* Check if the parameters are actually compatible */
for (size_t i=0; i<N; ++i)
if (buffers[i].size != 1 && (buffers[i].ndim != ndim || buffers[i].shape != shape))
- throw std::runtime_error("pybind11::vectorize: incompatible size/dimension of inputs!");
+ pybind11_fail("pybind11::vectorize: incompatible size/dimension of inputs!");
if (size == 1)
return cast(f(*((Args *) buffers[Index].ptr)...));
diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h
index 4c97c36..9865d44 100644
--- a/include/pybind11/operators.h
+++ b/include/pybind11/operators.h
@@ -45,17 +45,17 @@
/// Operator implementation generator
template <op_id id, op_type ot, typename L, typename R> struct op_ {
- template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, Extra&&... extra) const {
+ template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, const Extra&... extra) const {
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
typedef op_impl<id, ot, Base, L_type, R_type> op;
- class_.def(op::name(), &op::execute, std::forward<Extra>(extra)...);
+ class_.def(op::name(), &op::execute, extra...);
}
- template <typename Base, typename Holder, typename... Extra> void execute_cast(pybind11::class_<Base, Holder> &class_, Extra&&... extra) const {
+ template <typename Base, typename Holder, typename... Extra> void execute_cast(pybind11::class_<Base, Holder> &class_, const Extra&... extra) const {
typedef typename std::conditional<std::is_same<L, self_t>::value, Base, L>::type L_type;
typedef typename std::conditional<std::is_same<R, self_t>::value, Base, R>::type R_type;
typedef op_impl<id, ot, Base, L_type, R_type> op;
- class_.def(op::name(), &op::execute_cast, std::forward<Extra>(extra)...);
+ class_.def(op::name(), &op::execute_cast, extra...);
}
};
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index df43217..9fecaa9 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 <wenzel@inf.ethz.ch>
@@ -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 <typename T> struct arg_t;
-
-/// Annotation for keyword arguments
-struct arg {
- arg(const char *name) : name(name) { }
- template <typename T> arg_t<T> operator=(const T &value);
- const char *name;
-};
-
-/// Annotation for keyword arguments with default values
-template <typename T> 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 <typename T> arg_t<T> arg::operator=(const T &value) { return arg_t<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 <int Nurse, int Patient> struct keep_alive { };
-
-NAMESPACE_BEGIN(detail)
-
-/// Partial template helper to invoke function call policies (e.g. keep_alive) when a function is called
-template <typename... Args> struct process_dynamic;
-
-/// Default implementation: do nothing
-template <typename T> struct process_dynamic<T> {
- static void precall(PyObject *) { }
- static void postcall(PyObject *, PyObject *) { }
-};
-
-/// Recursively iterate over variadic template arguments
-template <typename T, typename... Args> struct process_dynamic<T, Args...> {
- static void precall(PyObject *arg) {
- process_dynamic<T>::precall(arg);
- process_dynamic<Args...>::precall(arg);
- }
- static void postcall(PyObject *arg, PyObject *ret) {
- process_dynamic<T>::postcall(arg, ret);
- process_dynamic<Args...>::postcall(arg, ret);
- }
-};
-
-template <> struct process_dynamic<> : public process_dynamic<void> { };
-
-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<detail::argument_entry> 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 <typename T> using return_value_caster =
detail::type_caster<typename std::conditional<
@@ -143,317 +39,135 @@
/// Picks a suitable argument value converter from cast.h
template <typename... T> using arg_value_caster =
detail::type_caster<typename std::tuple<T...>>;
-
- /// Deal with annotations that can be processed at function registration time
- template <typename... T> static void process_static(const std::tuple<T...> &args, function_entry *entry) {
- process_static(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
- }
-
- /// contd.
- template <typename... T, size_t ... Index> static void process_static(const std::tuple<T...> &args,
- function_entry *entry, detail::index_sequence<Index...>) {
- int unused[] = { 0, (process_static(std::get<Index>(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 <typename T>
- static void process_static(const pybind11::arg_t<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<typename detail::intrinsic_type<T>::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 <int Nurse, int Patient>
- static void process_static(const keep_alive<Nurse, Patient> &, function_entry *) { /* handled at call time */ }
public:
cpp_function() { }
/// Vanilla function pointers
template <typename Return, typename... Args, typename... Extra>
- 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<Args...> cast_in;
typedef return_value_caster<Return> 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<Extra...>::precall(pyArgs); // call policy precall
+ /* Invoke call policy pre-call hook */
+ detail::process_attributes<Extra...>::precall(pyArgs);
/* Do the call and convert the return value back into the Python domain */
- PyObject *result = cast_out::cast(
- args.template call<Return>((Return (*) (Args...)) entry->data),
- entry->policy, parent);
+ handle result = cast_out::cast(
+ args.template call<Return>((Return (*) (Args...)) rec->data),
+ rec->policy, parent);
- detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
+ /* Invoke call policy post-call hook */
+ detail::process_attributes<Extra...>::postcall(pyArgs, result);
+
return result;
};
- /* Process any user-provided function annotations */
- process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
+ /* Process any user-provided function attributes */
+ detail::process_attributes<Extra...>::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 <typename Func, typename... Extra> cpp_function(Func &&f, Extra&&... extra) {
+ template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) {
initialize(std::forward<Func>(f),
(typename detail::remove_class<decltype(
- &std::remove_reference<Func>::type::operator())>::type *) nullptr,
- std::forward<Extra>(extra)...);
+ &std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
}
/// Delegating helper constructor to deal with class methods (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra> 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>(extra)...);
+ (Return (*) (Class *, Arg...)) nullptr, extra...);
}
/// Delegating helper constructor to deal with class methods (const)
template <typename Return, typename Class, typename... Arg, typename... Extra> 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>(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 <typename Func, typename Return, typename... Args, typename... Extra>
- 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<Func>::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<Func>(f) };
+ auto rec = new detail::function_record();
+ rec->data = new capture { std::forward<Func>(f) };
/* Create a cleanup handler, but only if we have to (less generated code) */
if (!std::is_trivially_destructible<Func>::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<Args...> cast_in;
typedef return_value_caster<Return> 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<Extra...>::precall(pyArgs); // call policy precall
+ /* Invoke call policy pre-call hook */
+ detail::process_attributes<Extra...>::precall(pyArgs);
/* Do the call and convert the return value back into the Python domain */
- PyObject *result = cast_out::cast(
- args.template call<Return>(((capture *) entry->data)->f),
- entry->policy, parent);
+ handle result = cast_out::cast(
+ args.template call<Return>(((capture *) rec->data)->f),
+ rec->policy, parent);
- detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
+ /* Invoke call policy post-call hook */
+ detail::process_attributes<Extra...>::postcall(pyArgs, result);
+
return result;
};
- /* Process any user-provided function annotations */
- process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
+ /* Process any user-provided function attributes */
+ detail::process_attributes<Extra...>::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 @@
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 @@
#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<PyCFunction>(*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<PyCFunction>(*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 @@
}
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 @@
}
template <typename Func, typename... Extra>
- module &def(const char *name_, Func &&f, Extra&& ... extra) {
+ module &def(const char *name_, Func &&f, const Extra& ... extra) {
cpp_function func(std::forward<Func>(f), name(name_),
- sibling((handle) attr(name_)), std::forward<Extra>(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 @@
};
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 <typename type, typename holder_type> 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 @@
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 @@
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 @@
/* 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<void>, weakrefs);
@@ -722,12 +572,12 @@
#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 @@
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 @@
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 @@
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 <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
-template <typename... Args> struct init;
NAMESPACE_END(detail)
-template <typename type, typename holder_type = std::unique_ptr<type>> class class_ : public detail::generic_type {
+template <typename type, typename holder_type = std::unique_ptr<type>>
+class class_ : public detail::generic_type {
public:
typedef detail::instance<type, holder_type> 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 <typename... Extra>
+ 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<Extra...>::init(extra..., &record);
+
+ detail::generic_type::initialize(&record);
+ }
template <typename Func, typename... Extra>
- class_ &def(const char *name_, Func&& f, Extra&&... extra) {
+ class_ &def(const char *name_, Func&& f, const Extra&... extra) {
cpp_function cf(std::forward<Func>(f), name(name_),
- sibling(attr(name_)), is_method(this),
- std::forward<Extra>(extra)...);
+ sibling(attr(name_)), is_method(*this),
+ extra...);
attr(cf.name()) = cf;
return *this;
}
template <typename Func, typename... Extra> 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<Func>(f), name(name_),
- sibling(attr(name_)),
- std::forward<Extra>(extra)...);
+ sibling(attr(name_)), extra...);
attr(cf.name()) = cf;
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
- class_ &def(const detail::op_<id, ot, L, R> &op, Extra&&... extra) {
- op.template execute<type>(*this, std::forward<Extra>(extra)...);
+ class_ &def(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
+ op.template execute<type>(*this, extra...);
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
- class_ & def_cast(const detail::op_<id, ot, L, R> &op, Extra&&... extra) {
- op.template execute_cast<type>(*this, std::forward<Extra>(extra)...);
+ class_ & def_cast(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
+ op.template execute_cast<type>(*this, extra...);
return *this;
}
template <typename... Args, typename... Extra>
- class_ &def(const detail::init<Args...> &init, Extra&&... extra) {
- init.template execute<type>(*this, std::forward<Extra>(extra)...);
+ class_ &def(const detail::init<Args...> &init, const Extra&... extra) {
+ init.template execute<type>(*this, extra...);
return *this;
}
@@ -926,28 +778,28 @@
}
template <typename C, typename D, typename... Extra>
- 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 <typename C, typename D, typename... Extra>
- 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>(extra)...);
+ is_method(*this), extra...);
def_property_readonly(name, fget);
return *this;
}
template <typename D, typename... Extra>
- 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 @@
}
template <typename D, typename... Extra>
- 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>(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 @@
}
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<cpp_function&>(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<cpp_function&>(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 @@
/// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> {
public:
- enum_(object &scope, const char *name, const char *doc = nullptr)
- : class_<Type>(scope, name, doc), m_parent(scope) {
+ template <typename... Extra>
+ enum_(const handle &scope, const char *name, const Extra&... extra)
+ : class_<Type>(scope, name, extra...), m_parent(scope) {
auto entries = new std::unordered_map<int, const char *>();
this->def("__repr__", [name, entries](Type value) -> std::string {
auto it = entries->find((int) value);
@@ -1078,21 +929,21 @@
}
private:
std::unordered_map<int, const char *> *m_entries;
- object &m_parent;
+ handle m_parent;
};
NAMESPACE_BEGIN(detail)
template <typename... Args> struct init {
- template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, Extra&&... extra) const {
+ template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &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>(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 @@
(void) wr.release();
}
-template <int Nurse, int Patient> struct process_dynamic<keep_alive<Nurse, Patient>> : public process_dynamic<void> {
- template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
- static void precall(PyObject *arg) { keep_alive_impl(Nurse, Patient, arg, nullptr); }
- template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
- static void postcall(PyObject *, PyObject *) { }
- template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
- static void precall(PyObject *) { }
- template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
- static void postcall(PyObject *arg, PyObject *ret) { keep_alive_impl(Nurse, Patient, arg, ret); }
-};
-
NAMESPACE_END(detail)
template <typename... Args> detail::init<Args...> init() { return detail::init<Args...>(); };
@@ -1198,8 +1038,7 @@
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 10488c3..2ce6ed2 100644
--- a/include/pybind11/pytypes.h
+++ b/include/pybind11/pytypes.h
@@ -30,10 +30,11 @@
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 @@
template <typename T> T cast() const;
template <typename ... Args> 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 @@
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 @@
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 @@
};
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 @@
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 @@
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 @@
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 @@
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 @@
}
};
-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 c57befa..5153fec 100644
--- a/include/pybind11/stl.h
+++ b/include/pybind11/stl.h
@@ -26,7 +26,7 @@
typedef std::vector<Value, Alloc> type;
typedef type_caster<Value> 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 @@
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 @@
typedef std::set<Key, Compare, Alloc> type;
typedef type_caster<Key> 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 @@
typedef type_caster<Key> key_conv;
typedef type_caster<Value> 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 @@
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 691a51a..2f02e6a 100644
--- a/setup.py
+++ b/setup.py
@@ -15,6 +15,7 @@
packages=[],
license='BSD',
headers=[
+ 'include/pybind11/attr.h',
'include/pybind11/cast.h',
'include/pybind11/complex.h',
'include/pybind11/descr.h',
@@ -58,10 +59,10 @@
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.""")