Commit 48548ea4 by Wenzel Jakob

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
parent 9180519d
......@@ -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()
......
......@@ -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__``.
......
......@@ -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
......
......@@ -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
......
......@@ -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()
......
......@@ -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()
......
......@@ -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);
......
......@@ -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())
......
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
/*
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)
......@@ -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 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(PyObject *src, bool convert) {
if (src == nullptr || typeinfo == nullptr)
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))
return false;
if (PyTuple_Size(src) != size)
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;
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)
......@@ -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
......
......@@ -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());
}
......
......@@ -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<") +
......
......@@ -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)...));
......
......@@ -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...);
}
};
......
/*
pybind11/pybind11.h: Main header file of the C++11 python binding generator library
pybind11/pybind11.h: Main header file of the C++11 python
binding generator library
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
......@@ -10,131 +11,26 @@
#pragma once
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
#pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
#pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
# pragma warning(push)
# pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
# pragma warning(disable: 4800) // warning C4800: 'int': forcing value to bool 'true' or 'false' (performance warning)
# pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
# pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
# pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
#elif defined(__GNUG__) and !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
# pragma GCC diagnostic ignored "-Wunused-but-set-variable"
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#include "cast.h"
#include "attr.h"
NAMESPACE_BEGIN(pybind11)
template <typename T> struct arg_t;
/// Annotation for keyword arguments
struct arg {
arg(const char *name) : name(name) { }
template <typename T> arg_t<T> operator=(const T &value);
const char *name;
};
/// Annotation for keyword arguments with default values
template <typename T> struct arg_t : public arg {
arg_t(const char *name, const T &value, const char *descr = nullptr)
: arg(name), value(value), descr(descr) { }
T value;
const char *descr;
};
template <typename T> arg_t<T> arg::operator=(const T &value) { return arg_t<T>(name, value); }
/// Annotation for methods
struct is_method { PyObject *class_; is_method(object *o) : class_(o->ptr()) { } };
/// Annotation for documentation
struct doc { const char *value; doc(const char *value) : value(value) { } };
/// Annotation for function names
struct name { const char *value; name(const char *value) : value(value) { } };
/// Annotation indicating that a function is an overload associated with a given "sibling"
struct sibling { PyObject *value; sibling(handle value) : value(value.ptr()) { } };
/// Keep patient alive while nurse lives
template <int Nurse, int Patient> struct keep_alive { };
NAMESPACE_BEGIN(detail)
/// Partial template helper to invoke function call policies (e.g. keep_alive) when a function is called
template <typename... Args> struct process_dynamic;
/// Default implementation: do nothing
template <typename T> struct process_dynamic<T> {
static void precall(PyObject *) { }
static void postcall(PyObject *, PyObject *) { }
};
/// Recursively iterate over variadic template arguments
template <typename T, typename... Args> struct process_dynamic<T, Args...> {
static void precall(PyObject *arg) {
process_dynamic<T>::precall(arg);
process_dynamic<Args...>::precall(arg);
}
static void postcall(PyObject *arg, PyObject *ret) {
process_dynamic<T>::postcall(arg, ret);
process_dynamic<Args...>::postcall(arg, ret);
}
};
template <> struct process_dynamic<> : public process_dynamic<void> { };
NAMESPACE_END(detail)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function {
protected:
/// Special data structure which holds metadata about a bound function (signature, overloads, etc.)
struct function_entry {
/// Function name
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
// User-specified documentation string
char *doc = nullptr;
/// Human-readable version of the function signature
char *signature = nullptr;
/// List of registered keyword arguments
std::vector<detail::argument_entry> args;
/// Pointer to lambda function which converts arguments and performs the actual call
PyObject * (*impl) (function_entry *, PyObject *, PyObject *) = nullptr;
/// Storage for the wrapped function pointer and captured data, if any
void *data = nullptr;
/// Pointer to custom destructor for 'data' (if needed)
void (*free_data) (void *ptr) = nullptr;
/// Return value policy associated with this function
return_value_policy policy = return_value_policy::automatic;
/// True if name == '__init__'
bool is_constructor = false;
/// Python method object
PyMethodDef *def = nullptr;
/// Pointer to class (if this is method)
PyObject *class_ = nullptr;
/// Pointer to first registered function in overload chain
PyObject *sibling = nullptr;
/// Pointer to next overload
function_entry *next = nullptr;
};
function_entry *m_entry;
/// Picks a suitable return value converter from cast.h
template <typename T> using return_value_caster =
detail::type_caster<typename std::conditional<
......@@ -143,317 +39,135 @@ protected:
/// Picks a suitable argument value converter from cast.h
template <typename... T> using arg_value_caster =
detail::type_caster<typename std::tuple<T...>>;
/// Deal with annotations that can be processed at function registration time
template <typename... T> static void process_static(const std::tuple<T...> &args, function_entry *entry) {
process_static(args, entry, typename detail::make_index_sequence<sizeof...(T)>::type());
}
/// contd.
template <typename... T, size_t ... Index> static void process_static(const std::tuple<T...> &args,
function_entry *entry, detail::index_sequence<Index...>) {
int unused[] = { 0, (process_static(std::get<Index>(args), entry), 0)... };
(void) unused;
}
/* The following overloads are used to process any annotations passed to
cpp_function. They update the corresponding fields in m_entry */
/// Process an annotation specifying the function's name
static void process_static(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; }
/// Process an annotation specifying function's docstring (provided as a C-style string)
static void process_static(const char *doc, function_entry *entry) { entry->doc = (char *) doc; }
/// Process an annotation specifying function's docstring
static void process_static(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; }
/// Process an annotation indicating the function's return value policy
static void process_static(const pybind11::return_value_policy p, function_entry *entry) { entry->policy = p; }
/// Process an annotation which indicates that this is an overloaded function associated with a given sibling
static void process_static(const pybind11::sibling s, function_entry *entry) { entry->sibling = s.value; }
/// Process an annotation which indicates that this function is a method
static void process_static(const pybind11::is_method &m, function_entry *entry) { entry->class_ = m.class_; }
/// Process a keyword argument annotation (*without* a default value)
static void process_static(const pybind11::arg &a, function_entry *entry) {
if (entry->class_ && entry->args.empty())
entry->args.emplace_back("self", nullptr, nullptr);
entry->args.emplace_back(a.name, nullptr, nullptr);
}
/// Process a keyword argument annotation (with a default value)
template <typename T>
static void process_static(const pybind11::arg_t<T> &a, function_entry *entry) {
if (entry->class_ && entry->args.empty())
entry->args.emplace_back("self", nullptr, nullptr);
/* Convert keyword value into a Python object */
PyObject *obj = detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(
a.value, return_value_policy::automatic, nullptr);
if (obj == nullptr)
pybind11_fail("arg(): could not convert default keyword "
"argument into a Python object (type not "
"registered yet?)");
entry->args.emplace_back(a.name, a.descr, obj);
}
/// Process an annotation indicating a keep_alive call policy
template <int Nurse, int Patient>
static void process_static(const keep_alive<Nurse, Patient> &, function_entry *) { /* handled at call time */ }
public:
cpp_function() { }
/// Vanilla function pointers
template <typename Return, typename... Args, typename... Extra>
cpp_function(Return (*f)(Args...), Extra&&... extra) {
using detail::descr;
m_entry = new function_entry();
m_entry->data = (void *) f;
cpp_function(Return (*f)(Args...), const Extra&... extra) {
auto rec = new detail::function_record();
rec->data = (void *) f;
typedef arg_value_caster<Args...> cast_in;
typedef return_value_caster<Return> cast_out;
/* Dispatch code which converts function arguments and performs the actual function call */
m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject * {
rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle {
cast_in args;
/* Try to cast the function arguments into the C++ domain */
if (!args.load(pyArgs, true))
return PYBIND11_TRY_NEXT_OVERLOAD;
detail::process_dynamic<Extra...>::precall(pyArgs); // call policy precall
/* Invoke call policy pre-call hook */
detail::process_attributes<Extra...>::precall(pyArgs);
/* Do the call and convert the return value back into the Python domain */
PyObject *result = cast_out::cast(
args.template call<Return>((Return (*) (Args...)) entry->data),
entry->policy, parent);
handle result = cast_out::cast(
args.template call<Return>((Return (*) (Args...)) rec->data),
rec->policy, parent);
/* Invoke call policy post-call hook */
detail::process_attributes<Extra...>::postcall(pyArgs, result);
detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
return result;
};
/* Process any user-provided function annotations */
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
/* Process any user-provided function attributes */
detail::process_attributes<Extra...>::init(extra..., rec);
/* Generate a readable signature describing the function's arguments and return value types */
using detail::descr;
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
/* Register the function with Python from generic (non-templated) code */
initialize(signature.text(), signature.types(), sizeof...(Args));
initialize(rec, signature.text(), signature.types(), sizeof...(Args));
}
/// Delegating helper constructor to deal with lambda functions
template <typename Func, typename... Extra> cpp_function(Func &&f, Extra&&... extra) {
template <typename Func, typename... Extra> cpp_function(Func &&f, const Extra&... extra) {
initialize(std::forward<Func>(f),
(typename detail::remove_class<decltype(
&std::remove_reference<Func>::type::operator())>::type *) nullptr,
std::forward<Extra>(extra)...);
&std::remove_reference<Func>::type::operator())>::type *) nullptr, extra...);
}
/// Delegating helper constructor to deal with class methods (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
Return (Class::*f)(Arg...), Extra&&... extra) {
Return (Class::*f)(Arg...), const Extra&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, std::forward<Extra>(extra)...);
(Return (*) (Class *, Arg...)) nullptr, extra...);
}
/// Delegating helper constructor to deal with class methods (const)
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
Return (Class::*f)(Arg...) const, Extra&&... extra) {
Return (Class::*f)(Arg...) const, const Extra&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*)(const Class *, Arg ...)) nullptr, std::forward<Extra>(extra)...);
(Return (*)(const Class *, Arg ...)) nullptr, extra...);
}
/// Return the function name
const char *name() const { return m_entry->name; }
object name() const { return attr("__name__"); }
protected:
/// Special internal constructor for functors, lambda functions, etc.
template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...), Extra&&... extra) {
using detail::descr;
void initialize(Func &&f, Return (*)(Args...), const Extra&... extra) {
struct capture { typename std::remove_reference<Func>::type f; };
/* Store the function including any extra state it might have (e.g. a lambda capture object) */
m_entry = new function_entry();
m_entry->data = new capture { std::forward<Func>(f) };
auto rec = new detail::function_record();
rec->data = new capture { std::forward<Func>(f) };
/* Create a cleanup handler, but only if we have to (less generated code) */
if (!std::is_trivially_destructible<Func>::value)
m_entry->free_data = [](void *ptr) { delete (capture *) ptr; };
rec->free_data = [](void *ptr) { delete (capture *) ptr; };
else
m_entry->free_data = operator delete;
rec->free_data = operator delete;
typedef arg_value_caster<Args...> cast_in;
typedef return_value_caster<Return> cast_out;
/* Dispatch code which converts function arguments and performs the actual function call */
m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject *{
rec->impl = [](detail::function_record *rec, handle pyArgs, handle parent) -> handle {
cast_in args;
/* Try to cast the function arguments into the C++ domain */
if (!args.load(pyArgs, true))
return PYBIND11_TRY_NEXT_OVERLOAD;
detail::process_dynamic<Extra...>::precall(pyArgs); // call policy precall
/* Invoke call policy pre-call hook */
detail::process_attributes<Extra...>::precall(pyArgs);
/* Do the call and convert the return value back into the Python domain */
PyObject *result = cast_out::cast(
args.template call<Return>(((capture *) entry->data)->f),
entry->policy, parent);
handle result = cast_out::cast(
args.template call<Return>(((capture *) rec->data)->f),
rec->policy, parent);
/* Invoke call policy post-call hook */
detail::process_attributes<Extra...>::postcall(pyArgs, result);
detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
return result;
};
/* Process any user-provided function annotations */
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
/* Process any user-provided function attributes */
detail::process_attributes<Extra...>::init(extra..., rec);
/* Generate a readable signature describing the function's arguments and return value types */
using detail::descr;
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name();
/* Register the function with Python from generic (non-templated) code */
initialize(signature.text(), signature.types(), sizeof...(Args));
}
/// Main dispatch logic for calls to functions bound using pybind11
static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs) {
/* Iterator over the list of potentially admissible overloads */
function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr),
*it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
int nargs = (int) PyTuple_Size(args),
nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0;
PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr,
*result = PYBIND11_TRY_NEXT_OVERLOAD;
try {
for (; it != nullptr; it = it->next) {
object args_(args, true);
int kwargs_consumed = 0;
/* For each overload:
1. If the required list of arguments is longer than the
actually provided amount, create a copy of the argument
list and fill in any available keyword/default arguments.
2. Ensure that all keyword arguments were "consumed"
3. Call the function call dispatcher (function_entry::impl)
*/
if (nargs < (int) it->args.size()) {
args_ = object(PyTuple_New(it->args.size()), false);
for (int i = 0; i < nargs; ++i) {
PyObject *item = PyTuple_GET_ITEM(args, i);
Py_INCREF(item);
PyTuple_SET_ITEM(args_.ptr(), i, item);
}
int arg_ctr = 0;
for (auto const &it2 : it->args) {
int index = arg_ctr++;
if (PyTuple_GET_ITEM(args_.ptr(), index))
continue;
PyObject *value = nullptr;
if (kwargs)
value = PyDict_GetItemString(kwargs, it2.name);
if (value)
kwargs_consumed++;
else if (it2.value)
value = it2.value;
if (value) {
Py_INCREF(value);
PyTuple_SET_ITEM(args_.ptr(), index, value);
} else {
kwargs_consumed = -1; /* definite failure */
break;
}
}
}
if (kwargs_consumed == nkwargs)
result = it->impl(it, args_.ptr(), parent);
if (result != PYBIND11_TRY_NEXT_OVERLOAD)
break;
}
} catch (const error_already_set &) { return nullptr;
} catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr;
} catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr;
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return nullptr;
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr;
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return nullptr;
} catch (...) {
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
return nullptr;
}
if (result == PYBIND11_TRY_NEXT_OVERLOAD) {
std::string msg = "Incompatible function arguments. The "
"following argument types are supported:\n";
int ctr = 0;
for (function_entry *it2 = overloads; it2 != nullptr; it2 = it2->next) {
msg += " "+ std::to_string(++ctr) + ". ";
msg += it2->signature;
msg += "\n";
}
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
} else if (result == nullptr) {
std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t";
msg += it->signature;
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
} else {
if (overloads->is_constructor) {
/* When a construtor ran successfully, the corresponding
holder type (e.g. std::unique_ptr) must still be initialized. */
PyObject *inst = PyTuple_GetItem(args, 0);
auto tinfo = detail::get_type_info(Py_TYPE(inst));
tinfo->init_holder(inst, nullptr);
}
return result;
}
}
/// When a cpp_function is GCed, release any memory allocated by pybind11
static void destruct(function_entry *entry) {
while (entry) {
function_entry *next = entry->next;
if (entry->free_data)
entry->free_data(entry->data);
std::free((char *) entry->name);
std::free((char *) entry->doc);
std::free((char *) entry->signature);
for (auto &arg: entry->args) {
std::free((char *) arg.name);
std::free((char *) arg.descr);
Py_XDECREF(arg.value);
}
if (entry->def) {
std::free((char *) entry->def->ml_doc);
delete entry->def;
}
delete entry;
entry = next;
}
initialize(rec, signature.text(), signature.types(), sizeof...(Args));
}
/// Register a function call with Python (generic non-templated code goes here)
void initialize(const char *text, const std::type_info * const * types, int args) {
void initialize(detail::function_record *rec, const char *text,
const std::type_info *const *types, int args) {
/* Create copies of all referenced C-style strings */
m_entry->name = strdup(m_entry->name ? m_entry->name : "");
if (m_entry->doc) m_entry->doc = strdup(m_entry->doc);
for (auto &a: m_entry->args) {
rec->name = strdup(rec->name ? rec->name : "");
if (rec->doc) rec->doc = strdup(rec->doc);
for (auto &a: rec->args) {
if (a.name)
a.name = strdup(a.name);
if (a.descr)
......@@ -472,17 +186,17 @@ protected:
break;
if (c == '{') {
if (type_depth == 1 && arg_index < m_entry->args.size()) {
signature += m_entry->args[arg_index].name;
if (type_depth == 1 && arg_index < rec->args.size()) {
signature += rec->args[arg_index].name;
signature += " : ";
}
++type_depth;
} else if (c == '}') {
--type_depth;
if (type_depth == 1 && arg_index < m_entry->args.size()) {
if (m_entry->args[arg_index].descr) {
if (type_depth == 1 && arg_index < rec->args.size()) {
if (rec->args[arg_index].descr) {
signature += " = ";
signature += m_entry->args[arg_index].descr;
signature += rec->args[arg_index].descr;
}
arg_index++;
}
......@@ -511,67 +225,66 @@ protected:
#endif
#if PY_MAJOR_VERSION < 3
if (strcmp(m_entry->name, "__next__") == 0) {
std::free(m_entry->name);
m_entry->name = strdup("next");
if (strcmp(rec->name, "__next__") == 0) {
std::free(rec->name);
rec->name = strdup("next");
}
#endif
if (!m_entry->args.empty() && (int) m_entry->args.size() != args)
if (!rec->args.empty() && (int) rec->args.size() != args)
pybind11_fail(
"cpp_function(): function \"" + std::string(m_entry->name) + "\" takes " +
std::to_string(args) + " arguments, but " + std::to_string(m_entry->args.size()) +
"cpp_function(): function \"" + std::string(rec->name) + "\" takes " +
std::to_string(args) + " arguments, but " + std::to_string(rec->args.size()) +
" pybind11::arg entries were specified!");
m_entry->is_constructor = !strcmp(m_entry->name, "__init__");
m_entry->signature = strdup(signature.c_str());
m_entry->args.shrink_to_fit();
rec->is_constructor = !strcmp(rec->name, "__init__");
rec->signature = strdup(signature.c_str());
rec->args.shrink_to_fit();
#if PY_MAJOR_VERSION < 3
if (m_entry->sibling && PyMethod_Check(m_entry->sibling))
m_entry->sibling = PyMethod_GET_FUNCTION(m_entry->sibling);
if (rec->sibling && PyMethod_Check(rec->sibling.ptr()))
rec->sibling = PyMethod_GET_FUNCTION(rec->sibling.ptr());
#endif
function_entry *s_entry = nullptr, *entry = m_entry;
if (m_entry->sibling && PyCFunction_Check(m_entry->sibling)) {
capsule entry_capsule(PyCFunction_GetSelf(m_entry->sibling), true);
s_entry = (function_entry *) entry_capsule;
detail::function_record *chain = nullptr, *chain_start = rec;
if (rec->sibling && PyCFunction_Check(rec->sibling.ptr())) {
capsule rec_capsule(PyCFunction_GetSelf(rec->sibling.ptr()), true);
chain = (detail::function_record *) rec_capsule;
/* Never append a method to an overload chain of a parent class;
instead, hide the parent's overloads in this case */
if (s_entry->class_ != m_entry->class_)
s_entry = nullptr;
if (chain->class_ != rec->class_)
chain = nullptr;
}
if (!s_entry) {
if (!chain) {
/* No existing overload was found, create a new function object */
m_entry->def = new PyMethodDef();
memset(m_entry->def, 0, sizeof(PyMethodDef));
m_entry->def->ml_name = m_entry->name;
m_entry->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
m_entry->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule entry_capsule(m_entry, [](PyObject *o) {
destruct((function_entry *) PyCapsule_GetPointer(o, nullptr));
rec->def = new PyMethodDef();
memset(rec->def, 0, sizeof(PyMethodDef));
rec->def->ml_name = rec->name;
rec->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
rec->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule rec_capsule(rec, [](PyObject *o) {
destruct((detail::function_record *) PyCapsule_GetPointer(o, nullptr));
});
m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr());
m_ptr = PyCFunction_New(rec->def, rec_capsule.ptr());
if (!m_ptr)
pybind11_fail("cpp_function::cpp_function(): Could not allocate function object");
} else {
/* Append at the end of the overload chain */
m_ptr = m_entry->sibling;
m_ptr = rec->sibling.ptr();
inc_ref();
entry = s_entry;
while (s_entry->next)
s_entry = s_entry->next;
s_entry->next = m_entry;
chain_start = chain;
while (chain->next)
chain = chain->next;
chain->next = rec;
}
std::string signatures;
int index = 0;
function_entry *it = entry;
/* Create a nice pydoc entry including all signatures and
/* Create a nice pydoc rec including all signatures and
docstrings of the functions in the overload chain */
while (it) {
if (s_entry)
for (auto it = chain_start; it != nullptr; it = it->next) {
if (chain)
signatures += std::to_string(++index) + ". ";
signatures += "Signature : ";
signatures += it->signature;
......@@ -583,23 +296,150 @@ protected:
}
if (it->next)
signatures += "\n";
it = it->next;
}
/* Install docstring */
PyCFunctionObject *func = (PyCFunctionObject *) m_ptr;
if (func->m_ml->ml_doc)
std::free((char *) func->m_ml->ml_doc);
func->m_ml->ml_doc = strdup(signatures.c_str());
if (entry->class_) {
#if PY_MAJOR_VERSION >= 3
m_ptr = PyInstanceMethod_New(m_ptr);
#else
m_ptr = PyMethod_New(m_ptr, nullptr, entry->class_);
#endif
if (rec->class_) {
m_ptr = PYBIND11_INSTANCE_METHOD_NEW(m_ptr, rec->class_.ptr());
if (!m_ptr)
pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object");
Py_DECREF(func);
}
}
/// When a cpp_function is GCed, release any memory allocated by pybind11
static void destruct(detail::function_record *rec) {
while (rec) {
detail::function_record *next = rec->next;
if (rec->free_data)
rec->free_data(rec->data);
std::free((char *) rec->name);
std::free((char *) rec->doc);
std::free((char *) rec->signature);
for (auto &arg: rec->args) {
std::free((char *) arg.name);
std::free((char *) arg.descr);
arg.value.dec_ref();
}
if (rec->def) {
std::free((char *) rec->def->ml_doc);
delete rec->def;
}
delete rec;
rec = next;
}
}
/// Main dispatch logic for calls to functions bound using pybind11
static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs) {
/* Iterator over the list of potentially admissible overloads */
detail::function_record *overloads = (detail::function_record *) PyCapsule_GetPointer(self, nullptr),
*it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
int nargs = (int) PyTuple_Size(args),
nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0;
handle parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr,
result = PYBIND11_TRY_NEXT_OVERLOAD;
try {
for (; it != nullptr; it = it->next) {
tuple args_(args, true);
int kwargs_consumed = 0;
/* For each overload:
1. If the required list of arguments is longer than the
actually provided amount, create a copy of the argument
list and fill in any available keyword/default arguments.
2. Ensure that all keyword arguments were "consumed"
3. Call the function call dispatcher (function_record::impl)
*/
if (nargs < (int) it->args.size()) {
args_ = tuple(it->args.size());
for (int i = 0; i < nargs; ++i) {
handle item = PyTuple_GET_ITEM(args, i);
PyTuple_SET_ITEM(args_.ptr(), i, item.inc_ref().ptr());
}
int arg_ctr = 0;
for (auto const &it2 : it->args) {
int index = arg_ctr++;
if (PyTuple_GET_ITEM(args_.ptr(), index))
continue;
handle value;
if (kwargs)
value = PyDict_GetItemString(kwargs, it2.name);
if (value)
kwargs_consumed++;
else if (it2.value)
value = it2.value;
if (value) {
PyTuple_SET_ITEM(args_.ptr(), index, value.inc_ref().ptr());
} else {
kwargs_consumed = -1; /* definite failure */
break;
}
}
}
if (kwargs_consumed == nkwargs)
result = it->impl(it, args_, parent);
if (result.ptr() != PYBIND11_TRY_NEXT_OVERLOAD)
break;
}
} catch (const error_already_set &) { return nullptr;
} catch (const index_error &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr;
} catch (const stop_iteration &e) { PyErr_SetString(PyExc_StopIteration, e.what()); return nullptr;
} catch (const std::bad_alloc &e) { PyErr_SetString(PyExc_MemoryError, e.what()); return nullptr;
} catch (const std::domain_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::invalid_argument &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::length_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::out_of_range &e) { PyErr_SetString(PyExc_IndexError, e.what()); return nullptr;
} catch (const std::range_error &e) { PyErr_SetString(PyExc_ValueError, e.what()); return nullptr;
} catch (const std::exception &e) { PyErr_SetString(PyExc_RuntimeError, e.what()); return nullptr;
} catch (...) {
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
return nullptr;
}
if (result.ptr() == PYBIND11_TRY_NEXT_OVERLOAD) {
std::string msg = "Incompatible function arguments. The "
"following argument types are supported:\n";
int ctr = 0;
for (detail::function_record *it2 = overloads; it2 != nullptr; it2 = it2->next) {
msg += " "+ std::to_string(++ctr) + ". ";
msg += it2->signature;
msg += "\n";
}
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
} else if (!result) {
std::string msg = "Unable to convert function return value to a "
"Python type! The signature was\n\t";
msg += it->signature;
PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr;
} else {
if (overloads->is_constructor) {
/* When a construtor ran successfully, the corresponding
holder type (e.g. std::unique_ptr) must still be initialized. */
PyObject *inst = PyTuple_GetItem(args, 0);
auto tinfo = detail::get_type_info(Py_TYPE(inst));
tinfo->init_holder(inst, nullptr);
}
return result.ptr();
}
}
};
/// Wrapper for Python extension modules
......@@ -625,11 +465,11 @@ public:
}
template <typename Func, typename... Extra>
module &def(const char *name_, Func &&f, Extra&& ... extra) {
module &def(const char *name_, Func &&f, const Extra& ... extra) {
cpp_function func(std::forward<Func>(f), name(name_),
sibling((handle) attr(name_)), std::forward<Extra>(extra)...);
func.inc_ref(); /* The following line steals a reference to 'func' */
PyModule_AddObject(ptr(), name_, func.ptr());
sibling((handle) attr(name_)), extra...);
/* PyModule_AddObject steals a reference to 'func' */
PyModule_AddObject(ptr(), name_, func.inc_ref().ptr());
return *this;
}
......@@ -652,18 +492,27 @@ public:
};
NAMESPACE_BEGIN(detail)
/// Basic support for creating new Python heap types
/// Generic support for creating new Python heap types
class generic_type : public object {
template <typename type, typename holder_type> friend class class_;
public:
PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
generic_type(const object &scope, const char *name_, const std::type_info *type_cpp,
size_t type_size, size_t instance_size,
void (*init_holder)(PyObject *, const void *),
const destructor &dealloc, object parent, const char *doc) {
protected:
void initialize(type_record *rec) {
if (rec->base_type) {
if (rec->base_handle)
pybind11_fail("generic_type: specified base type multiple times!");
rec->base_handle = detail::get_type_handle(*(rec->base_type));
if (!rec->base_handle) {
std::string tname(rec->base_type->name());
detail::clean_type_id(tname);
pybind11_fail("generic_type: type \"" + std::string(rec->name) +
"\" referenced unknown base type \"" + tname + "\"");
}
}
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
object name(PYBIND11_FROM_STRING(name_), false);
object name(PYBIND11_FROM_STRING(rec->name), false);
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (!type_holder || !name)
......@@ -673,25 +522,26 @@ public:
auto &internals = get_internals();
detail::type_info *tinfo = new detail::type_info();
tinfo->type = (PyTypeObject *) type;
tinfo->type_size = type_size;
tinfo->init_holder = init_holder;
internals.registered_types_cpp[type_cpp] = tinfo;
tinfo->type_size = rec->type_size;
tinfo->init_holder = rec->init_holder;
internals.registered_types_cpp[rec->type] = tinfo;
internals.registered_types_py[type] = tinfo;
auto scope_module = (object) scope.attr("__module__");
auto scope_module = (object) rec->scope.attr("__module__");
if (!scope_module)
scope_module = (object) scope.attr("__name__");
scope_module = (object) rec->scope.attr("__name__");
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + name_)
: std::string(name_));
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + rec->name)
: std::string(rec->name));
/* Basic type attributes */
type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = instance_size;
type->ht_type.tp_base = (PyTypeObject *) parent.release();
type->ht_type.tp_basicsize = rec->instance_size;
type->ht_type.tp_base = (PyTypeObject *) rec->base_handle.ptr();
rec->base_handle.inc_ref();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
/* Qualified names for Python >= 3.3 */
auto scope_qualname = (object) scope.attr("__qualname__");
auto scope_qualname = (object) rec->scope.attr("__qualname__");
if (scope_qualname) {
type->ht_qualname = PyUnicode_FromFormat(
"%U.%U", scope_qualname.ptr(), name.ptr());
......@@ -700,7 +550,7 @@ public:
name.inc_ref();
}
#endif
type->ht_name = name.release();
type->ht_name = name.release().ptr();
/* Supported protocols */
type->ht_type.tp_as_number = &type->as_number;
......@@ -710,7 +560,7 @@ public:
/* Supported elementary operations */
type->ht_type.tp_init = (initproc) init;
type->ht_type.tp_new = (newfunc) new_instance;
type->ht_type.tp_dealloc = dealloc;
type->ht_type.tp_dealloc = rec->dealloc;
/* Support weak references (needed for the keep_alive feature) */
type->ht_type.tp_weaklistoffset = offsetof(instance<void>, weakrefs);
......@@ -722,12 +572,12 @@ public:
#endif
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
if (doc) {
if (rec->doc) {
/* Allocate memory for docstring (using PyObject_MALLOC, since
Python will free this later on) */
size_t size = strlen(doc) + 1;
size_t size = strlen(rec->doc) + 1;
type->ht_type.tp_doc = (char *) PyObject_MALLOC(size);
memcpy((void *) type->ht_type.tp_doc, doc, size);
memcpy((void *) type->ht_type.tp_doc, rec->doc, size);
}
if (PyType_Ready(&type->ht_type) < 0)
......@@ -736,15 +586,14 @@ public:
m_ptr = type_holder.ptr();
if (scope_module) // Needed by pydoc
type_holder.attr("__module__") = scope_module;
attr("__module__") = scope_module;
/* Register type with the parent scope */
scope.attr(name_) = *this;
rec->scope.attr(handle(type->ht_name)) = *this;
type_holder.release();
}
protected:
/// Allocate a metaclass on demand (for static properties)
handle metaclass() {
auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type;
......@@ -758,17 +607,21 @@ protected:
pybind11_fail("generic_type::metaclass(): unable to create type object!");
auto type = (PyHeapTypeObject*) type_holder.ptr();
type->ht_name = name.release();
type->ht_name = name.release().ptr();
#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
/* Qualified names for Python >= 3.3 */
type->ht_qualname = PyUnicode_FromFormat(
"%U__Meta", ((object) attr("__qualname__")).ptr());
#endif
type->ht_type.tp_name = strdup(name_.c_str());
type->ht_type.tp_base = &PyType_Type;
type->ht_type.tp_base = ob_type;
type->ht_type.tp_flags |= (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE) &
~Py_TPFLAGS_HAVE_GC;
if (PyType_Ready(&type->ht_type) < 0)
pybind11_fail("generic_type::metaclass(): PyType_Ready failed!");
ob_type = (PyTypeObject *) type_holder.release();
ob_type = (PyTypeObject *) type_holder.release().ptr();
}
return handle((PyObject *) ob_type);
}
......@@ -851,65 +704,64 @@ protected:
static void releasebuffer(PyObject *, Py_buffer *view) { delete (buffer_info *) view->internal; }
};
/* Forward declarations */
enum op_id : int;
enum op_type : int;
struct undefined_t;
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t> struct op_;
template <typename... Args> struct init;
NAMESPACE_END(detail)
template <typename type, typename holder_type = std::unique_ptr<type>> class class_ : public detail::generic_type {
template <typename type, typename holder_type = std::unique_ptr<type>>
class class_ : public detail::generic_type {
public:
typedef detail::instance<type, holder_type> instance_type;
PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
class_(object &scope, const char *name, const char *doc = nullptr)
: detail::generic_type(scope, name, &typeid(type), sizeof(type),
sizeof(instance_type), init_holder, dealloc,
object(), doc) { }
class_(object &scope, const char *name, object &parent,
const char *doc = nullptr)
: detail::generic_type(scope, name, &typeid(type), sizeof(type),
sizeof(instance_type), init_holder, dealloc,
parent, doc) { }
template <typename... Extra>
class_(handle scope, const char *name, const Extra &... extra) {
detail::type_record record;
record.scope = scope;
record.name = name;
record.type = &typeid(type);
record.type_size = sizeof(type);
record.instance_size = sizeof(instance_type);
record.init_holder = init_holder;
record.dealloc = dealloc;
/* Process optional arguments, if any */
detail::process_attributes<Extra...>::init(extra..., &record);
detail::generic_type::initialize(&record);
}
template <typename Func, typename... Extra>
class_ &def(const char *name_, Func&& f, Extra&&... extra) {
class_ &def(const char *name_, Func&& f, const Extra&... extra) {
cpp_function cf(std::forward<Func>(f), name(name_),
sibling(attr(name_)), is_method(this),
std::forward<Extra>(extra)...);
sibling(attr(name_)), is_method(*this),
extra...);
attr(cf.name()) = cf;
return *this;
}
template <typename Func, typename... Extra> class_ &
def_static(const char *name_, Func f, Extra&&... extra) {
def_static(const char *name_, Func f, const Extra&... extra) {
cpp_function cf(std::forward<Func>(f), name(name_),
sibling(attr(name_)),
std::forward<Extra>(extra)...);
sibling(attr(name_)), extra...);
attr(cf.name()) = cf;
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
class_ &def(const detail::op_<id, ot, L, R> &op, Extra&&... extra) {
op.template execute<type>(*this, std::forward<Extra>(extra)...);
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
op.template execute<type>(*this, extra...);
return *this;
}
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
class_ & def_cast(const detail::op_<id, ot, L, R> &op, Extra&&... extra) {
op.template execute_cast<type>(*this, std::forward<Extra>(extra)...);
class_ & def_cast(const detail::op_<id, ot, L, R> &op, const Extra&... extra) {
op.template execute_cast<type>(*this, extra...);
return *this;
}
template <typename... Args, typename... Extra>
class_ &def(const detail::init<Args...> &init, Extra&&... extra) {
init.template execute<type>(*this, std::forward<Extra>(extra)...);
class_ &def(const detail::init<Args...> &init, const Extra&... extra) {
init.template execute<type>(*this, extra...);
return *this;
}
......@@ -926,28 +778,28 @@ public:
}
template <typename C, typename D, typename... Extra>
class_ &def_readwrite(const char *name, D C::*pm, Extra&&... extra) {
class_ &def_readwrite(const char *name, D C::*pm, const Extra&... extra) {
cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; },
return_value_policy::reference_internal,
is_method(this), extra...),
is_method(*this), extra...),
fset([pm](C &c, const D &value) { c.*pm = value; },
is_method(this), extra...);
is_method(*this), extra...);
def_property(name, fget, fset);
return *this;
}
template <typename C, typename D, typename... Extra>
class_ &def_readonly(const char *name, const D C::*pm, Extra&& ...extra) {
class_ &def_readonly(const char *name, const D C::*pm, const Extra& ...extra) {
cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; },
return_value_policy::reference_internal,
is_method(this), std::forward<Extra>(extra)...);
is_method(*this), extra...);
def_property_readonly(name, fget);
return *this;
}
template <typename D, typename... Extra>
class_ &def_readwrite_static(const char *name, D *pm, Extra&& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr,
class_ &def_readwrite_static(const char *name, D *pm, const Extra& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; },
return_value_policy::reference_internal, extra...),
fset([pm](object, const D &value) { *pm = value; }, extra...);
def_property_static(name, fget, fset);
......@@ -955,9 +807,9 @@ public:
}
template <typename D, typename... Extra>
class_ &def_readonly_static(const char *name, const D *pm, Extra&& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; }, nullptr,
return_value_policy::reference_internal, std::forward<Extra>(extra)...);
class_ &def_readonly_static(const char *name, const D *pm, const Extra& ...extra) {
cpp_function fget([pm](object) -> const D &{ return *pm; },
return_value_policy::reference_internal, extra...);
def_property_readonly_static(name, fget);
return *this;
}
......@@ -973,21 +825,19 @@ public:
}
class_ &def_property(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
object doc_obj = doc ? pybind11::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
object doc_obj = doc ? pybind11::str(doc) : (object) fget.attr("__doc__");
object property(
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type,
fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
attr(name) = property;
return *this;
}
class_ &def_property_static(const char *name, const cpp_function &fget, const cpp_function &fset, const char *doc = nullptr) {
object doc_obj = doc ? pybind11::str(doc) : (object) const_cast<cpp_function&>(fget).attr("__doc__");
object doc_obj = doc ? pybind11::str(doc) : (object) fget.attr("__doc__");
object property(
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type,
fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
PyObject_CallFunctionObjArgs((PyObject *) &PyProperty_Type, fget.ptr() ? fget.ptr() : Py_None,
fset.ptr() ? fset.ptr() : Py_None, Py_None, doc_obj.ptr(), nullptr), false);
metaclass().attr(name) = property;
return *this;
}
......@@ -1047,8 +897,9 @@ private:
/// Binds C++ enumerations and enumeration classes to Python
template <typename Type> class enum_ : public class_<Type> {
public:
enum_(object &scope, const char *name, const char *doc = nullptr)
: class_<Type>(scope, name, doc), m_parent(scope) {
template <typename... Extra>
enum_(const handle &scope, const char *name, const Extra&... extra)
: class_<Type>(scope, name, extra...), m_parent(scope) {
auto entries = new std::unordered_map<int, const char *>();
this->def("__repr__", [name, entries](Type value) -> std::string {
auto it = entries->find((int) value);
......@@ -1078,21 +929,21 @@ public:
}
private:
std::unordered_map<int, const char *> *m_entries;
object &m_parent;
handle m_parent;
};
NAMESPACE_BEGIN(detail)
template <typename... Args> struct init {
template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, Extra&&... extra) const {
template <typename Base, typename Holder, typename... Extra> void execute(pybind11::class_<Base, Holder> &class_, const Extra&... extra) const {
/// Function which calls a specific C++ in-place constructor
class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, std::forward<Extra>(extra)...);
class_.def("__init__", [](Base *instance, Args... args) { new (instance) Base(args...); }, extra...);
}
};
PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *arg, PyObject *ret) {
PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, handle args, handle ret) {
/* Clever approach based on weak references taken from Boost.Python */
handle nurse (Nurse > 0 ? PyTuple_GetItem(arg, Nurse - 1) : ret);
handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret);
handle nurse (Nurse > 0 ? PyTuple_GetItem(args.ptr(), Nurse - 1) : ret.ptr());
handle patient(Patient > 0 ? PyTuple_GetItem(args.ptr(), Patient - 1) : ret.ptr());
if (!nurse || !patient)
pybind11_fail("Could not activate keep_alive!");
......@@ -1106,17 +957,6 @@ PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *
(void) wr.release();
}
template <int Nurse, int Patient> struct process_dynamic<keep_alive<Nurse, Patient>> : public process_dynamic<void> {
template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
static void precall(PyObject *arg) { keep_alive_impl(Nurse, Patient, arg, nullptr); }
template <int N = Nurse, int P = Patient, typename std::enable_if<N != 0 && P != 0, int>::type = 0>
static void postcall(PyObject *, PyObject *) { }
template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
static void precall(PyObject *) { }
template <int N = Nurse, int P = Patient, typename std::enable_if<N == 0 || P == 0, int>::type = 0>
static void postcall(PyObject *arg, PyObject *ret) { keep_alive_impl(Nurse, Patient, arg, ret); }
};
NAMESPACE_END(detail)
template <typename... Args> detail::init<Args...> init() { return detail::init<Args...>(); };
......@@ -1198,8 +1038,7 @@ inline function get_overload(const void *this_ptr, const char *name) {
NAMESPACE_END(pybind11)
#if defined(_MSC_VER)
#pragma warning(pop)
# pragma warning(pop)
#elif defined(__GNUG__) and !defined(__clang__)
#pragma GCC diagnostic pop
# pragma GCC diagnostic pop
#endif
......@@ -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)
......@@ -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();
......
......@@ -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.""")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment