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
This commit is contained in:
Wenzel Jakob 2016-01-17 22:36:44 +01:00
parent 9180519d8c
commit 48548ea4a5
20 changed files with 1001 additions and 851 deletions

View File

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

View File

@ -925,7 +925,7 @@ If not available, the signature may not be very helpful, e.g.:
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__``.

View File

@ -5,15 +5,19 @@ Changelog
1.2 (not yet released)
--------------------------
* Optional: efficient generation of function signatures at compile time using C++14
* Switched to a simpler and more general way of dealing with function default arguments
Unused keyword arguments in function calls are now detected and cause errors as expected
* Switched to a simpler and more general way of dealing with function default
arguments. Unused keyword arguments in function calls are now detected and
cause errors as expected
* New ``keep_alive`` call policy analogous to Boost.Python's ``with_custodian_and_ward``
* New ``pybind11::base<>`` attribute to indicate a subclass relationship
* Improved interface for RAII type wrappers in ``pytypes.h``
* Use RAII type wrappers consistently within pybind11 itself. This
fixes various potential refcount leaks when exceptions occur
* Added new ``bytes`` RAII type wrapper (maps to ``string`` in Python 2.7).
* Made handle and related RAII classes const correct
* Made handle and related RAII classes const correct, using them more
consistently everywhere now
* Got rid of the ugly ``__pybind11__`` attributes on the Python side---they are
now stored in a C++ hash table that is not visible in Python
* Fixed refcount leaks involving NumPy arrays and bound functions
@ -21,7 +25,9 @@ Changelog
* Removed an unnecessary copy operation in ``pybind11::vectorize``
* Fixed naming clashes when both pybind11 and NumPy headers are included
* Added conversions for additional exception types
* Documentation improvements (using multiple extension modules, smart pointers, other minor clarifications)
* Documentation improvements (using multiple extension modules, smart pointers,
other minor clarifications)
* unified infrastructure for parsing variadic arguments in class_ and cpp_function
* Fixed license text (was: ZLIB, should have been: 3-clause BSD)
* Python 3.2 compatibility

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +1,7 @@
Rabbit is a parrot
Polly is a parrot
Polly is a parrot
Molly is a dog
Molly is a dog
Woof!
The following error is expected: Incompatible function arguments. The following argument types are supported:
1. (Dog) -> NoneType
Callback function 1 called!
False
Callback function 2 called : Hello, x, True, 5
5
func(43) = 44
func(43) = 44
Payload constructor
Payload copy constructor
@ -18,3 +9,14 @@ Payload move constructor
Payload destructor
Payload destructor
Payload destructor
Rabbit is a parrot
Polly is a parrot
Molly is a dog
The following error is expected: Incompatible function arguments. The following argument types are supported:
1. (example.Dog) -> NoneType
Callback function 1 called!
False
Callback function 2 called : Hello, x, True, 5
5
func(43) = 44

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(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 @@ template <typename type, typename Enable = void> class type_caster : public type
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), &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);
}
@ -149,7 +222,7 @@ protected:
type value; \
public: \
static PYBIND11_DESCR name() { return type_descr(py_name); } \
static PyObject *cast(const type *src, return_value_policy policy, PyObject *parent) { \
static handle cast(const type *src, return_value_policy policy, handle parent) { \
return cast(*src, policy, parent); \
} \
operator type*() { return &value; } \
@ -171,21 +244,21 @@ struct type_caster<
typedef typename std::conditional<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 @@ public:
return true;
}
static PyObject *cast(T src, return_value_policy /* policy */, PyObject * /* parent */) {
static handle cast(T src, return_value_policy /* policy */, handle /* parent */) {
if (std::is_floating_point<T>::value) {
return PyFloat_FromDouble((double) src);
} else if (sizeof(T) <= sizeof(long)) {
@ -216,12 +289,13 @@ public:
}
}
static PyObject *cast(const T *src, return_value_policy policy, PyObject *parent) {
static handle cast(const T *src, return_value_policy policy, handle parent) {
return cast(*src, policy, parent);
}
template <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 @@ protected:
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<std::nullptr_t> : public type_caster<void_type> {
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 @@ public:
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 @@ protected:
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 @@ template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
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 @@ protected:
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 @@ public:
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 @@ public:
explicit operator holder_type&() { return holder; }
explicit operator holder_type*() { return &holder; }
static PyObject *cast(const holder_type &src, return_value_policy policy, PyObject *parent) {
static handle cast(const holder_type &src, return_value_policy policy, handle parent) {
return type_caster_generic::cast(
src.get(), policy, parent, &typeid(type), &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> {
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 <> inline void handle::cast() const { return; }
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)

View File

@ -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 @@ template <typename type, typename holder_type = std::unique_ptr<type>> struct in
holder_type holder;
};
/// Additional type information which does not fit into the PyTypeObject
struct type_info {
PyTypeObject *type;
size_t type_size;
void (*init_holder)(PyObject *, const void *);
std::vector<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 @@ struct overload_hash {
}
};
/// Stores information about a keyword argument
struct argument_entry {
const char *name; ///< Argument name
const char *descr; ///< Human-readable version of the argument value
PyObject *value; ///< Associated Python object
argument_entry(const char *name, const char *descr, PyObject *value)
: name(name), descr(descr), value(value) { }
};
/// Internal data struture used to track registered instances and types
struct internals {
std::unordered_map<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)
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 @@ public:
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());
}

View File

@ -18,14 +18,13 @@ NAMESPACE_BEGIN(detail)
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 @@ public:
}
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<") +

View File

@ -79,36 +79,36 @@ public:
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 @@ struct vectorize_helper {
/* 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)...));

View File

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

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

View File

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

View File

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