major cleanup of the whole codebase

- new pybind11::base<> attribute to indicate a subclass relationship
- unified infrastructure for parsing variadic arguments in class_ and cpp_function
- use 'handle' and 'object' more consistently everywhere
This commit is contained in:
Wenzel Jakob 2016-01-17 22:36:44 +01:00
parent 140546b135
commit ed3d23a5d6
20 changed files with 996 additions and 850 deletions

View File

@ -80,6 +80,7 @@ include_directories(${PYTHON_INCLUDE_DIR})
include_directories(include) include_directories(include)
set(PYBIND11_HEADERS set(PYBIND11_HEADERS
include/pybind11/attr.h
include/pybind11/cast.h include/pybind11/cast.h
include/pybind11/common.h include/pybind11/common.h
include/pybind11/complex.h include/pybind11/complex.h
@ -167,7 +168,7 @@ elseif (UNIX)
# Strip unnecessary sections of the binary on Linux/Mac OS # Strip unnecessary sections of the binary on Linux/Mac OS
if(APPLE) if(APPLE)
set_target_properties(example PROPERTIES MACOSX_RPATH ".") 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) 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) add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_SOURCE_DIR}/example/example.so)
endif() endif()

View File

@ -925,7 +925,7 @@ If not available, the signature may not be very helpful, e.g.:
FUNCTIONS FUNCTIONS
... ...
| myFunction(...) | 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__``. The first way of addressing this is by defining ``SomeType.__repr__``.

View File

@ -5,15 +5,19 @@ Changelog
1.2 (not yet released) 1.2 (not yet released)
-------------------------- --------------------------
* Optional: efficient generation of function signatures at compile time using C++14 * 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 * Switched to a simpler and more general way of dealing with function default
Unused keyword arguments in function calls are now detected and cause errors as expected 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 ``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`` * Improved interface for RAII type wrappers in ``pytypes.h``
* Use RAII type wrappers consistently within pybind11 itself. This * Use RAII type wrappers consistently within pybind11 itself. This
fixes various potential refcount leaks when exceptions occur fixes various potential refcount leaks when exceptions occur
* Added new ``bytes`` RAII type wrapper (maps to ``string`` in Python 2.7). * 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 * 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 now stored in a C++ hash table that is not visible in Python
* Fixed refcount leaks involving NumPy arrays and bound functions * Fixed refcount leaks involving NumPy arrays and bound functions
@ -21,7 +25,9 @@ Changelog
* Removed an unnecessary copy operation in ``pybind11::vectorize`` * Removed an unnecessary copy operation in ``pybind11::vectorize``
* Fixed naming clashes when both pybind11 and NumPy headers are included * Fixed naming clashes when both pybind11 and NumPy headers are included
* Added conversions for additional exception types * 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) * Fixed license text (was: ZLIB, should have been: 3-clause BSD)
* Python 3.2 compatibility * Python 3.2 compatibility

View File

@ -177,9 +177,22 @@ inheritance relationship:
std::string bark() const { return "woof!"; } std::string bark() const { return "woof!"; }
}; };
To capture the hierarchical relationship in pybind11, we must assign a name to There are two different ways of indicating a hierarchical relationship to
the ``Pet`` :class:`class_` instance and reference it when binding the ``Dog`` pybind11: the first is by specifying the C++ base class explicitly during
class. 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 .. code-block:: cpp
@ -187,11 +200,12 @@ class.
pet.def(py::init<const std::string &>()) pet.def(py::init<const std::string &>())
.def_readwrite("name", &Pet::name); .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(py::init<const std::string &>())
.def("bark", &Dog::bark); .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 .. code-block:: python
@ -242,14 +256,14 @@ The overload signatures are also visible in the method's docstring:
| Methods defined here: | Methods defined here:
| |
| __init__(...) | __init__(...)
| Signature : (Pet, str, int) -> None | Signature : (Pet, str, int) -> NoneType
| |
| set(...) | set(...)
| 1. Signature : (Pet, int) -> None | 1. Signature : (Pet, int) -> NoneType
| |
| Set the pet's age | Set the pet's age
| |
| 2. Signature : (Pet, str) -> None | 2. Signature : (Pet, str) -> NoneType
| |
| Set the pet's name | Set the pet's name

View File

@ -115,7 +115,7 @@ and that the pybind11 repository is located in a subdirectory named :file:`pybin
# Strip unnecessary sections of the binary on Linux/Mac OS # Strip unnecessary sections of the binary on Linux/Mac OS
if(APPLE) if(APPLE)
set_target_properties(example PROPERTIES MACOSX_RPATH ".") 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) if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_BINARY_DIR}/example.so) add_custom_command(TARGET example POST_BUILD COMMAND strip -u -r ${PROJECT_BINARY_DIR}/example.so)
endif() endif()

View File

@ -63,17 +63,19 @@ Without reference counting
Return the ``PyObject *`` underlying a :class:`handle`. 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 Manually increase the reference count of the Python object. Usually, it is
preferable to use the :class:`object` class which derives from 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 Manually decrease the reference count of the Python object. Usually, it is
preferable to use the :class:`object` class which derives from 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 .. function:: void handle::ref_count() const
@ -167,11 +169,11 @@ With reference counting
Move constructor; steals the object from ``other`` and preserves its Move constructor; steals the object from ``other`` and preserves its
reference count. reference count.
.. function:: PyObject* object::release() .. function:: handle object::release()
Release ownership of underlying ``PyObject *``. Returns raw Python object Resets the internal pointer to ``nullptr`` without without decreasing the
pointer without decreasing its reference count and resets handle to object's reference count. The function returns a raw handle to the original
``nullptr``-valued pointer. Python object.
.. function:: object::~object() .. function:: object::~object()

View File

@ -29,6 +29,11 @@ public:
void bark() const { std::cout << "Woof!" << std::endl; } 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) { void pet_print(const Pet &pet) {
std::cout << pet.name() + " is a " + pet.species() << std::endl; std::cout << pet.name() + " is a " + pet.species() << std::endl;
} }
@ -62,9 +67,14 @@ void init_ex5(py::module &m) {
.def("name", &Pet::name) .def("name", &Pet::name)
.def("species", &Pet::species); .def("species", &Pet::species);
/* One way of declaring a subclass relationship: reference parent's class_ object */
py::class_<Dog>(m, "Dog", pet_class) py::class_<Dog>(m, "Dog", pet_class)
.def(py::init<std::string>()); .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("pet_print", pet_print);
m.def("dog_bark", dog_bark); m.def("dog_bark", dog_bark);

View File

@ -5,11 +5,15 @@ sys.path.append('.')
from example import Pet from example import Pet
from example import Dog from example import Dog
from example import Rabbit
from example import dog_bark from example import dog_bark
from example import pet_print from example import pet_print
polly = Pet('Polly', 'parrot') polly = Pet('Polly', 'parrot')
molly = Dog('Molly') molly = Dog('Molly')
roger = Rabbit('Rabbit')
print(roger.name() + " is a " + roger.species())
pet_print(roger)
print(polly.name() + " is a " + polly.species()) print(polly.name() + " is a " + polly.species())
pet_print(polly) pet_print(polly)
print(molly.name() + " is a " + molly.species()) print(molly.name() + " is a " + molly.species())

View File

@ -1,16 +1,7 @@
Rabbit is a parrot
Polly is a parrot Polly is a parrot
Polly is a parrot
Molly is a dog
Molly is a dog Molly is a dog
Woof! 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 func(43) = 44
Payload constructor Payload constructor
Payload copy constructor Payload copy constructor
@ -18,3 +9,14 @@ Payload move constructor
Payload destructor Payload destructor
Payload destructor 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

275
include/pybind11/attr.h Normal file
View File

@ -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)

View File

@ -19,95 +19,168 @@
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
class type_caster_generic { /// Additional type information which does not fit into the PyTypeObject
public: struct type_info {
PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) { PyTypeObject *type;
auto &types = get_internals().registered_types_cpp; 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); PYBIND11_NOINLINE inline internals &get_internals() {
if (it != types.end()) { static internals *internals_ptr = nullptr;
typeinfo = (detail::type_info *) it->second; if (internals_ptr)
} else { return *internals_ptr;
/* Unknown type?! Since std::type_info* often varies across handle builtins(PyEval_GetBuiltins());
module boundaries, the following does an explicit check */ capsule caps(builtins["__pybind11__"]);
for (auto const &type : types) { if (caps.check()) {
auto *first = (const std::type_info *) type.first; internals_ptr = caps;
if (strcmp(first->name(), type_info->name()) == 0) { } else {
types[type_info] = type.second; internals_ptr = new internals();
typeinfo = (detail::type_info *) type.second; builtins["__pybind11__"] = capsule(internals_ptr);
break; }
} 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) { PYBIND11_NOINLINE inline handle get_type_handle(const std::type_info &tp) {
if (src == nullptr || typeinfo == nullptr) 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; return false;
if (PyType_IsSubtype(Py_TYPE(src), typeinfo->type)) { if (PyType_IsSubtype(Py_TYPE(src.ptr()), typeinfo->type)) {
value = ((instance<void> *) src)->value; value = ((instance<void> *) src.ptr())->value;
return true; return true;
} }
if (convert) { if (convert) {
for (auto &converter : typeinfo->implicit_conversions) { for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src, typeinfo->type), false); temp = object(converter(src.ptr(), typeinfo->type), false);
if (load(temp.ptr(), false)) if (load(temp, false))
return true; return true;
} }
} }
return false; return false;
} }
PYBIND11_NOINLINE static PyObject *cast(const void *_src, return_value_policy policy, PyObject *parent, PYBIND11_NOINLINE static handle cast(const void *_src, return_value_policy policy, handle parent,
const std::type_info *type_info, const std::type_info *type_info,
void *(*copy_constructor)(const void *), void *(*copy_constructor)(const void *),
const void *existing_holder = nullptr) { const void *existing_holder = nullptr) {
void *src = const_cast<void *>(_src); void *src = const_cast<void *>(_src);
if (src == nullptr) { if (src == nullptr)
Py_INCREF(Py_None); return handle(Py_None).inc_ref();
return Py_None;
}
// avoid an issue with internal references matching their parent's address // avoid an issue with internal references matching their parent's address
bool dont_cache = policy == return_value_policy::reference_internal && 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& internals = get_internals();
auto it_instance = internals.registered_instances.find(src); auto it_instance = internals.registered_instances.find(src);
if (it_instance != internals.registered_instances.end() && !dont_cache) { if (it_instance != internals.registered_instances.end() && !dont_cache)
PyObject *inst = (PyObject *) it_instance->second; return handle((PyObject *) it_instance->second).inc_ref();
Py_INCREF(inst);
return inst;
}
auto it = internals.registered_types_cpp.find(type_info); auto it = internals.registered_types_cpp.find(type_info);
if (it == internals.registered_types_cpp.end()) { if (it == internals.registered_types_cpp.end()) {
std::string tname = type_info->name(); std::string tname = type_info->name();
detail::clean_type_id(tname); detail::clean_type_id(tname);
std::string msg = "Unregistered type : " + tname; std::string msg = "Unregistered type : " + tname;
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return handle();
} }
auto tinfo = (const detail::type_info *) it->second; auto tinfo = (const detail::type_info *) it->second;
instance<void> *inst = (instance<void> *) PyType_GenericAlloc(tinfo->type, 0); object inst(PyType_GenericAlloc(tinfo->type, 0), false);
inst->value = src;
inst->owned = true; auto wrapper = (instance<void> *) inst.ptr();
inst->parent = nullptr;
wrapper->value = src;
wrapper->owned = true;
wrapper->parent = nullptr;
if (policy == return_value_policy::automatic) if (policy == return_value_policy::automatic)
policy = return_value_policy::take_ownership; policy = return_value_policy::take_ownership;
if (policy == return_value_policy::copy) { if (policy == return_value_policy::copy) {
inst->value = copy_constructor(inst->value); wrapper->value = copy_constructor(wrapper->value);
if (inst->value == nullptr) if (wrapper->value == nullptr)
throw cast_error("return_value_policy = copy, but the object is non-copyable!"); throw cast_error("return_value_policy = copy, but the object is non-copyable!");
} else if (policy == return_value_policy::reference) { } else if (policy == return_value_policy::reference) {
inst->owned = false; wrapper->owned = false;
} else if (policy == return_value_policy::reference_internal) { } else if (policy == return_value_policy::reference_internal) {
inst->owned = false; wrapper->owned = false;
inst->parent = parent; wrapper->parent = parent.inc_ref().ptr();
Py_XINCREF(parent);
} }
PyObject *inst_pyobj = (PyObject *) inst;
tinfo->init_holder(inst_pyobj, existing_holder); tinfo->init_holder(inst.ptr(), existing_holder);
if (!dont_cache) if (!dont_cache)
internals.registered_instances[inst->value] = inst_pyobj; internals.registered_instances[wrapper->value] = inst.ptr();
return inst_pyobj;
return inst.release();
} }
protected: protected:
@ -121,15 +194,15 @@ template <typename type, typename Enable = void> class type_caster : public type
public: public:
static PYBIND11_DESCR name() { return type_descr(_<type>()); } 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) if (policy == return_value_policy::automatic)
policy = return_value_policy::copy; policy = return_value_policy::copy;
return type_caster_generic::cast(&src, policy, parent, &typeid(type), &copy_constructor); return type_caster_generic::cast(&src, policy, parent, &typeid(type), &copy_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), &copy_constructor); return type_caster_generic::cast(src, policy, parent, &typeid(type), &copy_constructor);
} }
@ -149,7 +222,7 @@ protected:
type value; \ type value; \
public: \ public: \
static PYBIND11_DESCR name() { return type_descr(py_name); } \ 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); \ return cast(*src, policy, parent); \
} \ } \
operator type*() { return &value; } \ operator type*() { return &value; } \
@ -171,21 +244,21 @@ struct type_caster<
typedef typename std::conditional<std::is_floating_point<T>::value, double, _py_type_1>::type py_type; typedef typename std::conditional<std::is_floating_point<T>::value, double, _py_type_1>::type py_type;
public: public:
bool load(PyObject *src, bool) { bool load(handle src, bool) {
py_type py_value; py_type py_value;
if (std::is_floating_point<T>::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)) { } else if (sizeof(T) <= sizeof(long)) {
if (std::is_signed<T>::value) if (std::is_signed<T>::value)
py_value = (py_type) PyLong_AsLong(src); py_value = (py_type) PyLong_AsLong(src.ptr());
else else
py_value = (py_type) PyLong_AsUnsignedLong(src); py_value = (py_type) PyLong_AsUnsignedLong(src.ptr());
} else { } else {
if (std::is_signed<T>::value) 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 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()) || if ((py_value == (py_type) -1 && PyErr_Occurred()) ||
@ -200,7 +273,7 @@ public:
return true; 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) { if (std::is_floating_point<T>::value) {
return PyFloat_FromDouble((double) src); return PyFloat_FromDouble((double) src);
} else if (sizeof(T) <= sizeof(long)) { } else if (sizeof(T) <= sizeof(long)) {
@ -216,12 +289,13 @@ public:
} }
} }
static PyObject *cast(const T *src, return_value_policy policy, PyObject *parent) { static handle cast(const T *src, return_value_policy policy, handle parent) {
return cast(*src, policy, parent); return cast(*src, policy, parent);
} }
template <typename T2 = T, typename std::enable_if<std::is_integral<T2>::value, int>::type = 0> template <typename T2 = T, typename std::enable_if<std::is_integral<T2>::value, int>::type = 0>
static PYBIND11_DESCR name() { return type_descr(_("int")); } static PYBIND11_DESCR name() { return type_descr(_("int")); }
template <typename T2 = T, typename std::enable_if<!std::is_integral<T2>::value, int>::type = 0> template <typename T2 = T, typename std::enable_if<!std::is_integral<T2>::value, int>::type = 0>
static PYBIND11_DESCR name() { return type_descr(_("float")); } static PYBIND11_DESCR name() { return type_descr(_("float")); }
@ -234,10 +308,9 @@ protected:
template <> class type_caster<void_type> { template <> class type_caster<void_type> {
public: public:
bool load(PyObject *, bool) { return false; } bool load(handle, bool) { return false; }
static PyObject *cast(void_type, return_value_policy /* policy */, PyObject * /* parent */) { static handle cast(void_type, return_value_policy /* policy */, handle /* parent */) {
Py_INCREF(Py_None); return handle(Py_None).inc_ref();
return Py_None;
} }
PYBIND11_TYPE_CASTER(void_type, _("NoneType")); PYBIND11_TYPE_CASTER(void_type, _("NoneType"));
}; };
@ -247,38 +320,36 @@ template <> class type_caster<std::nullptr_t> : public type_caster<void_type> {
template <> class type_caster<bool> { template <> class type_caster<bool> {
public: public:
bool load(PyObject *src, bool) { bool load(handle src, bool) {
if (src == Py_True) { value = true; return true; } if (src.ptr() == Py_True) { value = true; return true; }
else if (src == Py_False) { value = false; return true; } else if (src.ptr() == Py_False) { value = false; return true; }
else return false; else return false;
} }
static PyObject *cast(bool src, return_value_policy /* policy */, PyObject * /* parent */) { static handle cast(bool src, return_value_policy /* policy */, handle /* parent */) {
PyObject *result = src ? Py_True : Py_False; return handle(src ? Py_True : Py_False).inc_ref();
Py_INCREF(result);
return result;
} }
PYBIND11_TYPE_CASTER(bool, _("bool")); PYBIND11_TYPE_CASTER(bool, _("bool"));
}; };
template <> class type_caster<std::string> { template <> class type_caster<std::string> {
public: public:
bool load(PyObject *src, bool) { bool load(handle src, bool) {
object temp; object temp;
PyObject *load_src = src; handle load_src = src;
if (PyUnicode_Check(src)) { if (PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_AsUTF8String(src), false); temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false);
if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
load_src = temp.ptr(); load_src = temp;
} }
char *buffer; char *buffer;
ssize_t length; 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 if (err == -1) { PyErr_Clear(); return false; } // TypeError
value = std::string(buffer, length); value = std::string(buffer, length);
return true; 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()); return PyUnicode_FromStringAndSize(src.c_str(), src.length());
} }
@ -287,25 +358,25 @@ public:
template <> class type_caster<char> { template <> class type_caster<char> {
public: public:
bool load(PyObject *src, bool) { bool load(handle src, bool) {
object temp; object temp;
PyObject *load_src = src; handle load_src = src;
if (PyUnicode_Check(src)) { if (PyUnicode_Check(load_src.ptr())) {
temp = object(PyUnicode_AsUTF8String(src), false); temp = object(PyUnicode_AsUTF8String(load_src.ptr()), false);
if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError 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 if (!ptr) { PyErr_Clear(); return false; } // TypeError
value = std::string(ptr); value = std::string(ptr);
return true; 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); 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' }; char str[2] = { src, '\0' };
return PyUnicode_DecodeLatin1(str, 1, nullptr); return PyUnicode_DecodeLatin1(str, 1, nullptr);
} }
@ -321,25 +392,22 @@ protected:
template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> { template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
typedef std::pair<T1, T2> type; typedef std::pair<T1, T2> type;
public: public:
bool load(PyObject *src, bool convert) { bool load(handle src, bool convert) {
if (!PyTuple_Check(src) || PyTuple_Size(src) != 2) if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != 2)
return false; return false;
if (!first.load(PyTuple_GET_ITEM(src, 0), convert)) return first.load(PyTuple_GET_ITEM(src.ptr(), 0), convert) &&
return false; second.load(PyTuple_GET_ITEM(src.ptr(), 1), convert);
return second.load(PyTuple_GET_ITEM(src, 1), convert);
} }
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { static handle cast(const type &src, return_value_policy policy, handle parent) {
object o1(type_caster<typename intrinsic_type<T1>::type>::cast(src.first, policy, parent), false); object o1 = object(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); object o2 = object(type_caster<typename intrinsic_type<T2>::type>::cast(src.second, policy, parent), false);
if (!o1 || !o2) if (!o1 || !o2)
return nullptr; return handle();
PyObject *tuple = PyTuple_New(2); tuple result(2);
if (!tuple) PyTuple_SET_ITEM(result.ptr(), 0, o1.release().ptr());
return nullptr; PyTuple_SET_ITEM(result.ptr(), 1, o2.release().ptr());
PyTuple_SET_ITEM(tuple, 0, o1.release()); return result.release();
PyTuple_SET_ITEM(tuple, 1, o2.release());
return tuple;
} }
static PYBIND11_DESCR name() { static PYBIND11_DESCR name() {
@ -361,11 +429,11 @@ template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
public: public:
enum { size = sizeof...(Tuple) }; 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()); 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()); return cast(src, policy, parent, typename make_index_sequence<size>::type());
} }
@ -398,36 +466,32 @@ protected:
return type((Tuple) std::get<Index>(value)...); return type((Tuple) std::get<Index>(value)...);
} }
template <size_t ... Indices> bool load(PyObject *src, bool convert, index_sequence<Indices...>) { template <size_t ... Indices> bool load(handle src, bool convert, index_sequence<Indices...>) {
if (!PyTuple_Check(src)) if (!PyTuple_Check(src.ptr()) || PyTuple_Size(src.ptr()) != size)
return false; return false;
if (PyTuple_Size(src) != size) std::array<bool, size> success {{
return false; (PyTuple_GET_ITEM(src.ptr(), Indices) != nullptr ? std::get<Indices>(value).load(PyTuple_GET_ITEM(src.ptr(), Indices), convert) : false)...
std::array<bool, size> results {{
(PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get<Indices>(value).load(PyTuple_GET_ITEM(src, Indices), convert) : false)...
}}; }};
(void) convert; /* avoid a warning when the tuple is empty */ (void) convert; /* avoid a warning when the tuple is empty */
for (bool r : results) for (bool r : success)
if (!r) if (!r)
return false; return false;
return true; return true;
} }
/* Implementation: Convert a C++ tuple into a Python tuple */ /* 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...>) { template <size_t ... Indices> static handle cast(const type &src, return_value_policy policy, handle parent, index_sequence<Indices...>) {
std::array<object, size> results {{ std::array<object, size> entries {{
object(type_caster<typename intrinsic_type<Tuple>::type>::cast(std::get<Indices>(src), policy, parent), false)... object(type_caster<typename intrinsic_type<Tuple>::type>::cast(std::get<Indices>(src), policy, parent), false)...
}}; }};
for (const auto & result : results) for (const auto &entry: entries)
if (!result) if (!entry)
return nullptr; return handle();
PyObject *tuple = PyTuple_New(size); tuple result(size);
if (!tuple)
return nullptr;
int counter = 0; int counter = 0;
for (auto & result : results) for (auto & entry: entries)
PyTuple_SET_ITEM(tuple, counter++, result.release()); PyTuple_SET_ITEM(result.ptr(), counter++, entry.release().ptr());
return tuple; return result.release();
} }
protected: protected:
@ -443,19 +507,21 @@ public:
using type_caster<type>::temp; using type_caster<type>::temp;
using type_caster<type>::copy_constructor; using type_caster<type>::copy_constructor;
bool load(PyObject *src, bool convert) { bool load(handle src, bool convert) {
if (src == nullptr || typeinfo == nullptr) if (!src || !typeinfo)
return false; 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; value = inst->value;
holder = inst->holder; holder = inst->holder;
return true; return true;
} }
if (convert) { if (convert) {
for (auto &converter : typeinfo->implicit_conversions) { for (auto &converter : typeinfo->implicit_conversions) {
temp = object(converter(src, typeinfo->type), false); temp = object(converter(src.ptr(), typeinfo->type), false);
if (load(temp.ptr(), false)) if (load(temp, false))
return true; return true;
} }
} }
@ -467,7 +533,7 @@ public:
explicit operator holder_type&() { return holder; } explicit operator holder_type&() { return holder; }
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( return type_caster_generic::cast(
src.get(), policy, parent, &typeid(type), &copy_constructor, &src); src.get(), policy, parent, &typeid(type), &copy_constructor, &src);
} }
@ -483,27 +549,27 @@ template <typename type>
struct type_caster<type, typename std::enable_if<std::is_base_of<handle, type>::value>::type> { struct type_caster<type, typename std::enable_if<std::is_base_of<handle, type>::value>::type> {
public: public:
template <typename T = type, typename std::enable_if<std::is_same<T, handle>::value, int>::type = 0> 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> 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 */) { static handle cast(const handle &src, return_value_policy /* policy */, handle /* parent */) {
src.inc_ref(); return (PyObject *) src.ptr(); return src.inc_ref();
} }
PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name()); PYBIND11_TYPE_CASTER(type, handle_type_name<type>::name());
}; };
NAMESPACE_END(detail) 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; 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"); throw cast_error("Unable to cast Python object to C++ type");
return conv; 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) if (policy == return_value_policy::automatic)
policy = std::is_pointer<T>::value ? return_value_policy::take_ownership : return_value_policy::copy; 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); return object(detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(value, policy, parent), false);
@ -514,23 +580,22 @@ template <> inline void handle::cast() const { return; }
template <typename... Args> inline object handle::call(Args&&... args_) const { template <typename... Args> inline object handle::call(Args&&... args_) const {
const size_t size = sizeof...(Args); 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( { object(detail::type_caster<typename detail::intrinsic_type<Args>::type>::cast(
std::forward<Args>(args_), return_value_policy::reference, nullptr), false)... } std::forward<Args>(args_), return_value_policy::reference, nullptr), false)... }
}; };
for (const auto & result : args) for (auto &arg_value : args)
if (!result) if (!arg_value)
throw cast_error("handle::call(): unable to convert input arguments to Python objects"); throw cast_error("handle::call(): unable to convert input "
object tuple(PyTuple_New(size), false); "arguments to Python objects");
if (!tuple) tuple args_tuple(size);
throw cast_error("handle::call(): unable to allocate tuple");
int counter = 0; int counter = 0;
for (auto & result : args) for (auto &arg_value : args)
PyTuple_SET_ITEM(tuple.ptr(), counter++, result.release()); PyTuple_SET_ITEM(args_tuple.ptr(), counter++, arg_value.release().ptr());
PyObject *result = PyObject_CallObject(m_ptr, tuple.ptr()); object result(PyObject_CallObject(m_ptr, args_tuple.ptr()), false);
if (result == nullptr && PyErr_Occurred()) if (!result)
throw error_already_set(); throw error_already_set();
return object(result, false); return result;
} }
NAMESPACE_END(pybind11) NAMESPACE_END(pybind11)

View File

@ -73,6 +73,7 @@
#include <memory> #include <memory>
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions #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_CHECK PyBytes_Check
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString #define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
@ -89,6 +90,7 @@
#define PYBIND11_PLUGIN_IMPL(name) \ #define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name() extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#else #else
#define PYBIND11_INSTANCE_METHOD_NEW(ptr, class_) PyMethod_New(ptr, nullptr, class_)
#define PYBIND11_BYTES_CHECK PyString_Check #define PYBIND11_BYTES_CHECK PyString_Check
#define PYBIND11_BYTES_FROM_STRING PyString_FromString #define PYBIND11_BYTES_FROM_STRING PyString_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize #define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
@ -200,16 +202,6 @@ template <typename type, typename holder_type = std::unique_ptr<type>> struct in
holder_type holder; 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 { struct overload_hash {
inline std::size_t operator()(const std::pair<const PyObject *, const char *>& v) const { inline std::size_t operator()(const std::pair<const PyObject *, const char *>& v) const {
size_t value = std::hash<const void *>()(v.first); size_t value = std::hash<const void *>()(v.first);
@ -218,16 +210,6 @@ struct overload_hash {
} }
}; };
/// Stores information about a keyword argument
struct argument_entry {
const char *name; ///< Argument name
const char *descr; ///< Human-readable version of the argument value
PyObject *value; ///< Associated Python object
argument_entry(const char *name, const char *descr, PyObject *value)
: name(name), descr(descr), value(value) { }
};
/// Internal data struture used to track registered instances and types /// Internal data struture used to track registered instances and types
struct internals { struct internals {
std::unordered_map<const void *, void*> registered_types_cpp; // std::type_info* -> type_info std::unordered_map<const void *, void*> registered_types_cpp; // std::type_info* -> type_info

View File

@ -20,8 +20,8 @@ PYBIND11_DECL_FMT(std::complex<double>, "Zd");
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
template <typename T> class type_caster<std::complex<T>> { template <typename T> class type_caster<std::complex<T>> {
public: public:
bool load(PyObject *src, bool) { bool load(handle src, bool) {
Py_complex result = PyComplex_AsCComplex(src); Py_complex result = PyComplex_AsCComplex(src.ptr());
if (result.real == -1.0 && PyErr_Occurred()) { if (result.real == -1.0 && PyErr_Occurred()) {
PyErr_Clear(); PyErr_Clear();
return false; return false;
@ -30,7 +30,7 @@ public:
return true; 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()); return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
} }

View File

@ -18,14 +18,13 @@ NAMESPACE_BEGIN(detail)
template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> { template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> {
typedef std::function<Return(Args...)> type; typedef std::function<Return(Args...)> type;
public: public:
bool load(handle src_, bool) {
bool load(PyObject *src_, bool) {
src_ = detail::get_function(src_); 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; return false;
object src(src_, true); object src(src_, true);
value = [src](Args... args) -> Return { 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 */ /* Visual studio 2015 parser issue: need parentheses around this expression */
return (retval.template cast<Return>()); return (retval.template cast<Return>());
}; };
@ -33,10 +32,8 @@ public:
} }
template <typename Func> template <typename Func>
static PyObject *cast(Func &&f_, return_value_policy policy, PyObject *) { static handle cast(Func &&f_, return_value_policy policy, handle /* parent */) {
cpp_function f(std::forward<Func>(f_), policy); return cpp_function(std::forward<Func>(f_), policy).release();
f.inc_ref();
return f.ptr();
} }
PYBIND11_TYPE_CASTER(type, _("function<") + PYBIND11_TYPE_CASTER(type, _("function<") +

View File

@ -79,36 +79,36 @@ public:
API& api = lookup_api(); API& api = lookup_api();
PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<Type>::value); PyObject *descr = api.PyArray_DescrFromType_(npy_format_descriptor<Type>::value);
if (descr == nullptr) 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; Py_intptr_t shape = (Py_intptr_t) size;
object tmp = object(api.PyArray_NewFromDescr_( object tmp = object(api.PyArray_NewFromDescr_(
api.PyArray_Type_, descr, 1, &shape, nullptr, (void *) ptr, 0, nullptr), false); api.PyArray_Type_, descr, 1, &shape, nullptr, (void *) ptr, 0, nullptr), false);
if (ptr && tmp) if (ptr && tmp)
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false);
if (!tmp) if (!tmp)
throw std::runtime_error("NumPy: unable to create array!"); pybind11_fail("NumPy: unable to create array!");
m_ptr = tmp.release(); m_ptr = tmp.release().ptr();
} }
array(const buffer_info &info) { array(const buffer_info &info) {
API& api = lookup_api(); API& api = lookup_api();
if ((info.format.size() < 1) || (info.format.size() > 2)) 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]; int fmt = (int) info.format[0];
if (info.format == "Zd") fmt = API::NPY_CDOUBLE_; if (info.format == "Zd") fmt = API::NPY_CDOUBLE_;
else if (info.format == "Zf") fmt = API::NPY_CFLOAT_; else if (info.format == "Zf") fmt = API::NPY_CFLOAT_;
PyObject *descr = api.PyArray_DescrFromType_(fmt); PyObject *descr = api.PyArray_DescrFromType_(fmt);
if (descr == nullptr) 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_( object tmp(api.PyArray_NewFromDescr_(
api.PyArray_Type_, descr, info.ndim, (Py_intptr_t *) &info.shape[0], api.PyArray_Type_, descr, info.ndim, (Py_intptr_t *) &info.shape[0],
(Py_intptr_t *) &info.strides[0], info.ptr, 0, nullptr), false); (Py_intptr_t *) &info.strides[0], info.ptr, 0, nullptr), false);
if (info.ptr && tmp) if (info.ptr && tmp)
tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false); tmp = object(api.PyArray_NewCopy_(tmp.ptr(), -1 /* any order */), false);
if (!tmp) if (!tmp)
throw std::runtime_error("NumPy: unable to create array!"); pybind11_fail("NumPy: unable to create array!");
m_ptr = tmp.release(); m_ptr = tmp.release().ptr();
} }
protected: protected:
@ -186,7 +186,7 @@ struct vectorize_helper {
/* Check if the parameters are actually compatible */ /* Check if the parameters are actually compatible */
for (size_t i=0; i<N; ++i) for (size_t i=0; i<N; ++i)
if (buffers[i].size != 1 && (buffers[i].ndim != ndim || buffers[i].shape != shape)) 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) if (size == 1)
return cast(f(*((Args *) buffers[Index].ptr)...)); return cast(f(*((Args *) buffers[Index].ptr)...));

View File

@ -45,17 +45,17 @@ template <op_id, op_type, typename B, typename L, typename R> struct op_impl { }
/// Operator implementation generator /// Operator implementation generator
template <op_id id, op_type ot, typename L, typename R> struct op_ { 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<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 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; 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<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 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; 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...);
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@ -30,10 +30,11 @@ public:
handle(const handle &other) : m_ptr(other.m_ptr) { } handle(const handle &other) : m_ptr(other.m_ptr) { }
handle(PyObject *ptr) : m_ptr(ptr) { } handle(PyObject *ptr) : m_ptr(ptr) { }
PyObject *ptr() const { return m_ptr; } PyObject *ptr() const { return m_ptr; }
void inc_ref() const { Py_XINCREF(m_ptr); } PyObject *&ptr() { return m_ptr; }
void dec_ref() const { Py_XDECREF(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); } 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 begin() const;
inline iterator end() const; inline iterator end() const;
inline detail::accessor operator[](handle key) const; inline detail::accessor operator[](handle key) const;
@ -44,6 +45,8 @@ public:
template <typename T> T cast() const; template <typename T> T cast() const;
template <typename ... Args> object call(Args&&... args_) const; template <typename ... Args> object call(Args&&... args_) const;
operator bool() const { return m_ptr != nullptr; } 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; } bool check() const { return m_ptr != nullptr; }
protected: protected:
PyObject *m_ptr; PyObject *m_ptr;
@ -59,25 +62,25 @@ public:
object(object &&other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; } object(object &&other) { m_ptr = other.m_ptr; other.m_ptr = nullptr; }
~object() { dec_ref(); } ~object() { dec_ref(); }
PyObject * release() { handle release() {
PyObject *tmp = m_ptr; PyObject *tmp = m_ptr;
m_ptr = nullptr; m_ptr = nullptr;
return tmp; return handle(tmp);
} }
object& operator=(object &other) { object& operator=(object &other) {
Py_XINCREF(other.m_ptr); other.inc_ref();
Py_XDECREF(m_ptr); dec_ref();
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
return *this; return *this;
} }
object& operator=(object &&other) { object& operator=(object &&other) {
if (this != &other) { if (this != &other) {
PyObject *temp = m_ptr; handle temp(m_ptr);
m_ptr = other.m_ptr; m_ptr = other.m_ptr;
other.m_ptr = nullptr; other.m_ptr = nullptr;
Py_XDECREF(temp); temp.dec_ref();
} }
return *this; return *this;
} }
@ -85,7 +88,7 @@ public:
class iterator : public object { class iterator : public object {
public: public:
iterator(PyObject *obj, bool borrowed = false) : object(obj, borrowed) { ++*this; } iterator(handle obj, bool borrowed = false) : object(obj, borrowed) { ++*this; }
iterator& operator++() { iterator& operator++() {
if (ptr()) if (ptr())
value = object(PyIter_Next(m_ptr), false); value = object(PyIter_Next(m_ptr), false);
@ -100,108 +103,106 @@ private:
}; };
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
inline PyObject *get_function(PyObject *value) { inline handle get_function(handle value) {
if (value == nullptr) if (value) {
return nullptr;
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
if (PyInstanceMethod_Check(value)) if (PyInstanceMethod_Check(value.ptr()))
value = PyInstanceMethod_GET_FUNCTION(value); value = PyInstanceMethod_GET_FUNCTION(value.ptr());
#endif #endif
if (PyMethod_Check(value)) if (PyMethod_Check(value.ptr()))
value = PyMethod_GET_FUNCTION(value); value = PyMethod_GET_FUNCTION(value.ptr());
}
return value; return value;
} }
class accessor { class accessor {
public: public:
accessor(PyObject *obj, PyObject *key, bool attr) accessor(handle obj, handle key, bool attr)
: obj(obj), key(key), attr(attr) { Py_INCREF(key); } : obj(obj), key(key, true), attr(attr) { }
accessor(PyObject *obj, const char *key, bool attr) accessor(handle obj, const char *key, bool attr)
: obj(obj), key(PyUnicode_FromString(key)), attr(attr) { } : obj(obj), key(PyUnicode_FromString(key), false), attr(attr) { }
accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr) accessor(const accessor &a) : obj(a.obj), key(a.key), attr(a.attr) { }
{ Py_INCREF(key); }
~accessor() { Py_DECREF(key); }
void operator=(accessor o) { operator=(object(o)); } void operator=(accessor o) { operator=(object(o)); }
void operator=(const handle &h) { void operator=(const handle &value) {
if (attr) { 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"); pybind11_fail("Unable to set object attribute");
} else { } 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"); pybind11_fail("Unable to set object item");
} }
} }
operator object() const { operator object() const {
object result(attr ? PyObject_GetAttr(obj, key) object result(attr ? PyObject_GetAttr(obj.ptr(), key.ptr())
: PyObject_GetItem(obj, key), false); : PyObject_GetItem(obj.ptr(), key.ptr()), false);
if (!result) PyErr_Clear(); if (!result) {PyErr_Clear(); }
return result; return result;
} }
operator bool() const { operator bool() const {
if (attr) { if (attr) {
return (bool) PyObject_HasAttr(obj, key); return (bool) PyObject_HasAttr(obj.ptr(), key.ptr());
} else { } else {
object result(PyObject_GetItem(obj, key), false); object result(PyObject_GetItem(obj.ptr(), key.ptr()), false);
if (!result) PyErr_Clear(); if (!result) PyErr_Clear();
return (bool) result; return (bool) result;
} }
}; };
private: private:
PyObject *obj; handle obj;
PyObject *key; object key;
bool attr; bool attr;
}; };
struct list_accessor { struct list_accessor {
public: 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=(list_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) { void operator=(const handle &o) {
o.inc_ref(); // PyList_SetItem steals a reference // PyList_SetItem steals a reference to 'o'
if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0) if (PyList_SetItem(list.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
pybind11_fail("Unable to assign value in Python list!"); pybind11_fail("Unable to assign value in Python list!");
} }
operator object() const { operator object() const {
PyObject *result = PyList_GetItem(list, (ssize_t) index); PyObject *result = PyList_GetItem(list.ptr(), (ssize_t) index);
if (!result) if (!result)
pybind11_fail("Unable to retrieve value from Python list!"); pybind11_fail("Unable to retrieve value from Python list!");
return object(result, true); return object(result, true);
} }
private: private:
PyObject *list; handle list;
size_t index; size_t index;
}; };
struct tuple_accessor { struct tuple_accessor {
public: 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=(tuple_accessor o) { return operator=(object(o)); }
void operator=(const handle &o) { void operator=(const handle &o) {
o.inc_ref(); // PyTuple_SetItem steals a reference // PyTuple_SetItem steals a referenceto 'o'
if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0) if (PyTuple_SetItem(tuple.ptr(), (ssize_t) index, o.inc_ref().ptr()) < 0)
pybind11_fail("Unable to assign value in Python tuple!"); pybind11_fail("Unable to assign value in Python tuple!");
} }
operator object() const { operator object() const {
PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index); PyObject *result = PyTuple_GetItem(tuple.ptr(), (ssize_t) index);
if (!result) if (!result)
pybind11_fail("Unable to retrieve value from Python tuple!"); pybind11_fail("Unable to retrieve value from Python tuple!");
return object(result, true); return object(result, true);
} }
private: private:
PyObject *tuple; handle tuple;
size_t index; size_t index;
}; };
struct dict_iterator { struct dict_iterator {
public: 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++() { dict_iterator& operator++() {
if (!PyDict_Next(dict, &pos, &key, &value)) if (!PyDict_Next(dict.ptr(), &pos, &key.ptr(), &value.ptr()))
pos = -1; pos = -1;
return *this; return *this;
} }
@ -211,7 +212,7 @@ public:
bool operator==(const dict_iterator &it) const { return it.pos == pos; } bool operator==(const dict_iterator &it) const { return it.pos == pos; }
bool operator!=(const dict_iterator &it) const { return it.pos != pos; } bool operator!=(const dict_iterator &it) const { return it.pos != pos; }
private: private:
PyObject *dict, *key, *value; handle dict, key, value;
ssize_t pos = 0; ssize_t pos = 0;
}; };
@ -410,8 +411,8 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate list object!"); if (!m_ptr) pybind11_fail("Could not allocate list object!");
} }
size_t size() const { return (size_t) PyList_Size(m_ptr); } 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); } detail::list_accessor operator[](size_t index) const { return detail::list_accessor(*this, index); }
void append(const object &object) const { PyList_Append(m_ptr, (PyObject *) object.ptr()); } void append(const object &object) const { PyList_Append(m_ptr, object.ptr()); }
}; };
class set : public object { class set : public object {
@ -421,7 +422,7 @@ public:
if (!m_ptr) pybind11_fail("Could not allocate set object!"); if (!m_ptr) pybind11_fail("Could not allocate set object!");
} }
size_t size() const { return (size_t) PySet_Size(m_ptr); } 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); } void clear() const { PySet_Clear(m_ptr); }
}; };
@ -429,8 +430,8 @@ class function : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check) PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check)
bool is_cpp_function() const { bool is_cpp_function() const {
PyObject *ptr = detail::get_function(m_ptr); handle fun = detail::get_function(m_ptr);
return ptr != nullptr && PyCFunction_Check(ptr); return fun && PyCFunction_Check(fun.ptr());
} }
}; };
@ -448,57 +449,4 @@ public:
} }
}; };
NAMESPACE_BEGIN(detail)
PYBIND11_NOINLINE inline internals &get_internals() {
static internals *internals_ptr = nullptr;
if (internals_ptr)
return *internals_ptr;
handle builtins(PyEval_GetBuiltins());
capsule caps(builtins["__pybind11__"]);
if (caps.check()) {
internals_ptr = caps;
} else {
internals_ptr = new internals();
builtins["__pybind11__"] = capsule(internals_ptr);
}
return *internals_ptr;
}
PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
auto const &type_dict = get_internals().registered_types_py;
do {
auto it = type_dict.find(type);
if (it != type_dict.end())
return (detail::type_info *) it->second;
type = type->tp_base;
if (type == nullptr)
pybind11_fail("pybind11::detail::get_type_info: unable to find type object!");
} while (true);
}
PYBIND11_NOINLINE inline std::string error_string() {
std::string errorString;
PyThreadState *tstate = PyThreadState_GET();
if (tstate == nullptr)
return "";
if (tstate->curexc_type) {
errorString += (std::string) handle(tstate->curexc_type).str();
errorString += ": ";
}
if (tstate->curexc_value)
errorString += (std::string) handle(tstate->curexc_value).str();
return errorString;
}
PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr) {
auto instances = get_internals().registered_instances;
auto it = instances.find(ptr);
if (it == instances.end())
return handle();
return handle((PyObject *) it->second);
}
NAMESPACE_END(detail)
NAMESPACE_END(pybind11) NAMESPACE_END(pybind11)

View File

@ -26,7 +26,7 @@ template <typename Value, typename Alloc> struct type_caster<std::vector<Value,
typedef std::vector<Value, Alloc> type; typedef std::vector<Value, Alloc> type;
typedef type_caster<Value> value_conv; typedef type_caster<Value> value_conv;
public: public:
bool load(PyObject *src, bool convert) { bool load(handle src, bool convert) {
list l(src, true); list l(src, true);
if (!l.check()) if (!l.check())
return false; return false;
@ -34,21 +34,21 @@ public:
value.clear(); value.clear();
value_conv conv; value_conv conv;
for (auto it : l) { for (auto it : l) {
if (!conv.load(it.ptr(), convert)) if (!conv.load(it, convert))
return false; return false;
value.push_back((Value) conv); value.push_back((Value) conv);
} }
return true; 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()); list l(src.size());
size_t index = 0; size_t index = 0;
for (auto const &value: src) { 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_) if (!value_)
return nullptr; return handle();
PyList_SET_ITEM(l.ptr(), index++, value_.release()); // steals a reference PyList_SET_ITEM(l.ptr(), index++, value_.release().ptr()); // steals a reference
} }
return l.release(); return l.release();
} }
@ -59,26 +59,26 @@ template <typename Key, typename Compare, typename Alloc> struct type_caster<std
typedef std::set<Key, Compare, Alloc> type; typedef std::set<Key, Compare, Alloc> type;
typedef type_caster<Key> key_conv; typedef type_caster<Key> key_conv;
public: public:
bool load(PyObject *src, bool convert) { bool load(handle src, bool convert) {
pybind11::set s(src, true); pybind11::set s(src, true);
if (!s.check()) if (!s.check())
return false; return false;
value.clear(); value.clear();
key_conv conv; key_conv conv;
for (auto entry : s) { for (auto entry : s) {
if (!conv.load(entry.ptr(), convert)) if (!conv.load(entry, convert))
return false; return false;
value.insert((Key) conv); value.insert((Key) conv);
} }
return true; 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; pybind11::set s;
for (auto const &value: src) { 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)) if (!value_ || !s.add(value))
return nullptr; return handle();
} }
return s.release(); return s.release();
} }
@ -91,7 +91,7 @@ public:
typedef type_caster<Key> key_conv; typedef type_caster<Key> key_conv;
typedef type_caster<Value> value_conv; typedef type_caster<Value> value_conv;
bool load(PyObject *src, bool convert) { bool load(handle src, bool convert) {
dict d(src, true); dict d(src, true);
if (!d.check()) if (!d.check())
return false; return false;
@ -107,13 +107,13 @@ public:
return true; 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; dict d;
for (auto const &kv: src) { for (auto const &kv: src) {
object key(key_conv::cast(kv.first, policy, parent), false); object key = object(key_conv::cast(kv.first, policy, parent), false);
object value(value_conv::cast(kv.second, policy, parent), false); object value = object(value_conv::cast(kv.second, policy, parent), false);
if (!key || !value) if (!key || !value)
return nullptr; return handle();
d[key] = value; d[key] = value;
} }
return d.release(); return d.release();

View File

@ -15,6 +15,7 @@ setup(
packages=[], packages=[],
license='BSD', license='BSD',
headers=[ headers=[
'include/pybind11/attr.h',
'include/pybind11/cast.h', 'include/pybind11/cast.h',
'include/pybind11/complex.h', 'include/pybind11/complex.h',
'include/pybind11/descr.h', 'include/pybind11/descr.h',
@ -58,10 +59,10 @@ C++11-compatible compilers are widely available, this heavy machinery has
become an excessively large and unnecessary dependency. become an excessively large and unnecessary dependency.
Think of this library as a tiny self-contained version of Boost.Python with 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 everything stripped away that isn't relevant for binding generation. Without
header files only require ~2.5K lines of code and depend on Python (2.7 or 3.x) comments, the core header files only require ~2.5K lines of code and depend on
and the C++ standard library. This compact implementation was possible thanks Python (2.7 or 3.x) and the C++ standard library. This compact implementation
to some of the new C++11 language features (specifically: tuples, lambda was possible thanks to some of the new C++11 language features (specifically:
functions and variadic templates). Since its creation, this library has grown tuples, lambda functions and variadic templates). Since its creation, this
beyond Boost.Python in many ways, leading to dramatically simpler binding code library has grown beyond Boost.Python in many ways, leading to dramatically
in many common situations.""") simpler binding code in many common situations.""")