fully moved __pybind11__ Python attributes to the C++ side, cleanup & documentation pass over the main header file

This commit is contained in:
Wenzel Jakob 2016-01-17 22:36:41 +01:00
parent e45b29047a
commit d561cb010c
4 changed files with 299 additions and 177 deletions

View File

@ -22,17 +22,19 @@ NAMESPACE_BEGIN(detail)
class type_caster_generic { class type_caster_generic {
public: public:
PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) { PYBIND11_NOINLINE type_caster_generic(const std::type_info *type_info) {
auto & registered_types = get_internals().registered_types; auto &types = get_internals().registered_types_cpp;
auto it = registered_types.find(type_info);
if (it != registered_types.end()) { auto it = types.find(type_info);
typeinfo = &it->second; if (it != types.end()) {
typeinfo = (detail::type_info *) it->second;
} else { } else {
/* Unknown type?! Since std::type_info* often varies across /* Unknown type?! Since std::type_info* often varies across
module boundaries, the following does an explicit check */ module boundaries, the following does an explicit check */
for (auto const &type : registered_types) { for (auto const &type : types) {
if (strcmp(type.first->name(), type_info->name()) == 0) { auto *first = (const std::type_info *) type.first;
registered_types[type_info] = type.second; if (strcmp(first->name(), type_info->name()) == 0) {
typeinfo = &type.second; types[type_info] = type.second;
typeinfo = (detail::type_info *) type.second;
break; break;
} }
} }
@ -71,20 +73,20 @@ public:
auto& internals = get_internals(); auto& internals = get_internals();
auto it_instance = internals.registered_instances.find(src); auto it_instance = internals.registered_instances.find(src);
if (it_instance != internals.registered_instances.end() && !dont_cache) { if (it_instance != internals.registered_instances.end() && !dont_cache) {
PyObject *inst = it_instance->second; PyObject *inst = (PyObject *) it_instance->second;
Py_INCREF(inst); Py_INCREF(inst);
return inst; return inst;
} }
auto it = internals.registered_types.find(type_info); auto it = internals.registered_types_cpp.find(type_info);
if (it == internals.registered_types.end()) { if (it == internals.registered_types_cpp.end()) {
std::string tname = type_info->name(); std::string tname = type_info->name();
detail::clean_type_id(tname); detail::clean_type_id(tname);
std::string msg = "Unregistered type : " + tname; std::string msg = "Unregistered type : " + tname;
PyErr_SetString(PyExc_TypeError, msg.c_str()); PyErr_SetString(PyExc_TypeError, msg.c_str());
return nullptr; return nullptr;
} }
auto &reg_type = it->second; auto tinfo = (const detail::type_info *) it->second;
instance<void> *inst = (instance<void> *) PyType_GenericAlloc(reg_type.type, 0); instance<void> *inst = (instance<void> *) PyType_GenericAlloc(tinfo->type, 0);
inst->value = src; inst->value = src;
inst->owned = true; inst->owned = true;
inst->parent = nullptr; inst->parent = nullptr;
@ -102,7 +104,7 @@ public:
Py_XINCREF(parent); Py_XINCREF(parent);
} }
PyObject *inst_pyobj = (PyObject *) inst; PyObject *inst_pyobj = (PyObject *) inst;
reg_type.init_holder(inst_pyobj, existing_holder); tinfo->init_holder(inst_pyobj, existing_holder);
if (!dont_cache) if (!dont_cache)
internals.registered_instances[inst->value] = inst_pyobj; internals.registered_instances[inst->value] = inst_pyobj;
return inst_pyobj; return inst_pyobj;

View File

@ -66,25 +66,6 @@
#pragma warning(pop) #pragma warning(pop)
#endif #endif
#if PY_MAJOR_VERSION >= 3
#define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#else
#define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *init##name()
#endif
#define PYBIND11_PLUGIN(name) \
static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \
try { \
return pybind11_init(); \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \
} \
PyObject *pybind11_init()
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions #if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_BYTES_CHECK PyBytes_Check #define PYBIND11_BYTES_CHECK PyBytes_Check
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString #define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
@ -97,6 +78,10 @@
#define PYBIND11_BYTES_NAME "bytes" #define PYBIND11_BYTES_NAME "bytes"
#define PYBIND11_STRING_NAME "str" #define PYBIND11_STRING_NAME "str"
#define PYBIND11_SLICE_OBJECT PyObject #define PYBIND11_SLICE_OBJECT PyObject
#define PYBIND11_FROM_STRING PyUnicode_FromString
#define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_base.ob_base.ob_type
#define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *PyInit_##name()
#else #else
#define PYBIND11_BYTES_CHECK PyString_Check #define PYBIND11_BYTES_CHECK PyString_Check
#define PYBIND11_BYTES_FROM_STRING PyString_FromString #define PYBIND11_BYTES_FROM_STRING PyString_FromString
@ -109,8 +94,27 @@
#define PYBIND11_BYTES_NAME "str" #define PYBIND11_BYTES_NAME "str"
#define PYBIND11_STRING_NAME "unicode" #define PYBIND11_STRING_NAME "unicode"
#define PYBIND11_SLICE_OBJECT PySliceObject #define PYBIND11_SLICE_OBJECT PySliceObject
#define PYBIND11_FROM_STRING PyString_FromString
#define PYBIND11_OB_TYPE(ht_type) (ht_type).ob_type
#define PYBIND11_PLUGIN_IMPL(name) \
extern "C" PYBIND11_EXPORT PyObject *init##name()
#endif #endif
#define PYBIND11_TRY_NEXT_OVERLOAD ((PyObject *) 1) // special failure return code
#define PYBIND11_PLUGIN(name) \
static PyObject *pybind11_init(); \
PYBIND11_PLUGIN_IMPL(name) { \
try { \
return pybind11_init(); \
} catch (const std::exception &e) { \
PyErr_SetString(PyExc_ImportError, e.what()); \
return nullptr; \
} \
} \
PyObject *pybind11_init()
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
typedef Py_ssize_t ssize_t; typedef Py_ssize_t ssize_t;
@ -220,8 +224,9 @@ struct argument_entry {
/// Internal data struture used to track registered instances and types /// Internal data struture used to track registered instances and types
struct internals { struct internals {
std::unordered_map<const std::type_info *, type_info> registered_types; std::unordered_map<const void *, void*> registered_types_cpp; // std::type_info* -> type_info
std::unordered_map<const void *, PyObject *> registered_instances; std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
std::unordered_map<const void *, void*> registered_instances; // void * -> PyObject*
std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache; std::unordered_set<std::pair<const PyObject *, const char *>, overload_hash> inactive_overload_cache;
}; };

View File

@ -24,6 +24,7 @@
#endif #endif
#include "cast.h" #include "cast.h"
#include <iostream>
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
@ -55,19 +56,24 @@ struct doc { const char *value; doc(const char *value) : value(value) { } };
/// Annotation for function names /// Annotation for function names
struct name { const char *value; name(const char *value) : value(value) { } }; struct name { const char *value; name(const char *value) : value(value) { } };
/// Annotation for function siblings /// Annotation indicating that a function is an overload associated with a given "sibling"
struct sibling { PyObject *value; sibling(handle value) : value(value.ptr()) { } }; struct sibling { PyObject *value; sibling(handle value) : value(value.ptr()) { } };
/// Keep patient alive while nurse lives /// Keep patient alive while nurse lives
template <int Nurse, int Patient> struct keep_alive { }; template <int Nurse, int Patient> struct keep_alive { };
NAMESPACE_BEGIN(detail) 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; template <typename... Args> struct process_dynamic;
/// Default implementation: do nothing
template <typename T> struct process_dynamic<T> { template <typename T> struct process_dynamic<T> {
static void precall(PyObject *) { } static void precall(PyObject *) { }
static void postcall(PyObject *, PyObject *) { } static void postcall(PyObject *, PyObject *) { }
}; };
template <> struct process_dynamic<> : public process_dynamic<void> { };
/// Recursively iterate over variadic template arguments
template <typename T, typename... Args> struct process_dynamic<T, Args...> { template <typename T, typename... Args> struct process_dynamic<T, Args...> {
static void precall(PyObject *arg) { static void precall(PyObject *arg) {
process_dynamic<T>::precall(arg); process_dynamic<T>::precall(arg);
@ -78,35 +84,52 @@ template <typename T, typename... Args> struct process_dynamic<T, Args...> {
process_dynamic<Args...>::postcall(arg, ret); process_dynamic<Args...>::postcall(arg, ret);
} }
}; };
template <> struct process_dynamic<> : public process_dynamic<void> { };
NAMESPACE_END(detail) NAMESPACE_END(detail)
/// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
class cpp_function : public function { class cpp_function : public function {
private: protected:
/// Linked list of function overloads /// Special data structure which holds metadata about a bound function (signature, overloads, etc.)
struct function_entry { struct function_entry {
/// Function name and user-specified documentation string /// Function name
char *name = nullptr, *doc = nullptr; /* why no C++ strings? They generate heavier code.. */ 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 /// Human-readable version of the function signature
char *signature = nullptr; char *signature = nullptr;
/// List of registered keyword arguments /// List of registered keyword arguments
std::vector<detail::argument_entry> args; std::vector<detail::argument_entry> args;
/// Pointer to lambda function which converts arguments and performs the actual call /// Pointer to lambda function which converts arguments and performs the actual call
PyObject * (*impl) (function_entry *, PyObject *, PyObject *) = nullptr; PyObject * (*impl) (function_entry *, PyObject *, PyObject *) = nullptr;
/// Storage for the wrapped function pointer and captured data, if any /// Storage for the wrapped function pointer and captured data, if any
void *data = nullptr; void *data = nullptr;
/// Pointer to custom destructor for 'data' (if needed) /// Pointer to custom destructor for 'data' (if needed)
void (*free_data) (void *ptr) = nullptr; void (*free_data) (void *ptr) = nullptr;
/// Return value policy associated with this function /// Return value policy associated with this function
return_value_policy policy = return_value_policy::automatic; return_value_policy policy = return_value_policy::automatic;
/// True if name == '__init__' /// True if name == '__init__'
bool is_constructor = false; bool is_constructor = false;
/// Python method object /// Python method object
PyMethodDef *def = nullptr; PyMethodDef *def = nullptr;
/// Pointer to class (if this is method) /// Pointer to class (if this is method)
PyObject *class_ = nullptr; PyObject *class_ = nullptr;
/// Pointer to first registered function in overload chain /// Pointer to first registered function in overload chain
PyObject *sibling = nullptr; PyObject *sibling = nullptr;
/// Pointer to next overload /// Pointer to next overload
function_entry *next = nullptr; function_entry *next = nullptr;
}; };
@ -122,35 +145,53 @@ private:
template <typename... T> using arg_value_caster = template <typename... T> using arg_value_caster =
detail::type_caster<typename std::tuple<T...>>; 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) { 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()); 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, template <typename... T, size_t ... Index> static void process_static(const std::tuple<T...> &args,
function_entry *entry, detail::index_sequence<Index...>) { function_entry *entry, detail::index_sequence<Index...>) {
int unused[] = { 0, (process_static(std::get<Index>(args), entry), 0)... }; int unused[] = { 0, (process_static(std::get<Index>(args), entry), 0)... };
(void) unused; (void) unused;
} }
template <int Nurse, int Patient> /* The following overloads are used to process any annotations passed to
static void process_static(const keep_alive<Nurse, Patient> &, function_entry *) { } cpp_function. They update the corresponding fields in m_entry */
static void process_static(const char *doc, function_entry *entry) { entry->doc = (char *) doc; }
static void process_static(const pybind11::doc &d, function_entry *entry) { entry->doc = (char *) d.value; } /// Process an annotation specifying the function's name
static void process_static(const pybind11::name &n, function_entry *entry) { entry->name = (char *) n.value; } 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; } 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; } 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_; } 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) { static void process_static(const pybind11::arg &a, function_entry *entry) {
if (entry->class_ && entry->args.empty()) if (entry->class_ && entry->args.empty())
entry->args.emplace_back("self", nullptr, nullptr); entry->args.emplace_back("self", nullptr, nullptr);
entry->args.emplace_back(a.name, nullptr, nullptr); entry->args.emplace_back(a.name, nullptr, nullptr);
} }
/// Process a keyword argument annotation (with a default value)
template <typename T> template <typename T>
static void process_static(const pybind11::arg_t<T> &a, function_entry *entry) { static void process_static(const pybind11::arg_t<T> &a, function_entry *entry) {
if (entry->class_ && entry->args.empty()) if (entry->class_ && entry->args.empty())
entry->args.emplace_back("self", nullptr, nullptr); 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( PyObject *obj = detail::type_caster<typename detail::intrinsic_type<T>::type>::cast(
a.value, return_value_policy::automatic, nullptr); a.value, return_value_policy::automatic, nullptr);
@ -161,6 +202,10 @@ private:
entry->args.emplace_back(a.name, a.descr, obj); 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: public:
cpp_function() { } cpp_function() { }
@ -174,18 +219,32 @@ public:
typedef arg_value_caster<Args...> cast_in; typedef arg_value_caster<Args...> cast_in;
typedef return_value_caster<Return> cast_out; 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 * { m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject * {
cast_in args; cast_in args;
/* Try to cast the function arguments into the C++ domain */
if (!args.load(pyArgs, true)) if (!args.load(pyArgs, true))
return (PyObject *) 1; /* Special return code: try next overload */ return PYBIND11_TRY_NEXT_OVERLOAD;
detail::process_dynamic<Extra...>::precall(pyArgs);
PyObject *result = cast_out::cast(args.template call<Return>((Return (*)(Args...)) entry->data), entry->policy, parent); detail::process_dynamic<Extra...>::precall(pyArgs); // call policy precall
detail::process_dynamic<Extra...>::postcall(pyArgs, result);
/* 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);
detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
return result; return result;
}; };
/* Process any user-provided function annotations */
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry); process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
/* Generate a readable signature describing the function's arguments and return value types */
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); 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(signature.text(), signature.types(), sizeof...(Args));
} }
@ -197,14 +256,14 @@ public:
std::forward<Extra>(extra)...); std::forward<Extra>(extra)...);
} }
/// Class methods (non-const) /// Delegating helper constructor to deal with class methods (non-const)
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function( template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
Return (Class::*f)(Arg...), Extra&&... extra) { Return (Class::*f)(Arg...), Extra&&... extra) {
initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](Class *c, Arg... args) -> Return { return (c->*f)(args...); },
(Return (*) (Class *, Arg...)) nullptr, std::forward<Extra>(extra)...); (Return (*) (Class *, Arg...)) nullptr, std::forward<Extra>(extra)...);
} }
/// Class methods (const) /// Delegating helper constructor to deal with class methods (const)
template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function( template <typename Return, typename Class, typename... Arg, typename... Extra> cpp_function(
Return (Class::*f)(Arg...) const, Extra&&... extra) { Return (Class::*f)(Arg...) const, Extra&&... extra) {
initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); }, initialize([f](const Class *c, Arg... args) -> Return { return (c->*f)(args...); },
@ -214,17 +273,18 @@ public:
/// Return the function name /// Return the function name
const char *name() const { return m_entry->name; } const char *name() const { return m_entry->name; }
private: protected:
/// Functors, lambda functions, etc. /// Special internal constructor for functors, lambda functions, etc.
template <typename Func, typename Return, typename... Args, typename... Extra> template <typename Func, typename Return, typename... Args, typename... Extra>
void initialize(Func &&f, Return (*)(Args...), Extra&&... extra) { void initialize(Func &&f, Return (*)(Args...), Extra&&... extra) {
using detail::descr; using detail::descr;
struct capture { typename std::remove_reference<Func>::type f; }; 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 = new function_entry();
m_entry->data = new capture { std::forward<Func>(f) }; m_entry->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) if (!std::is_trivially_destructible<Func>::value)
m_entry->free_data = [](void *ptr) { delete (capture *) ptr; }; m_entry->free_data = [](void *ptr) { delete (capture *) ptr; };
else else
@ -233,33 +293,60 @@ private:
typedef arg_value_caster<Args...> cast_in; typedef arg_value_caster<Args...> cast_in;
typedef return_value_caster<Return> cast_out; 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 *{ m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *parent) -> PyObject *{
cast_in args; cast_in args;
/* Try to cast the function arguments into the C++ domain */
if (!args.load(pyArgs, true)) if (!args.load(pyArgs, true))
return (PyObject *) 1; /* Special return code: try next overload */ return PYBIND11_TRY_NEXT_OVERLOAD;
detail::process_dynamic<Extra...>::precall(pyArgs);
PyObject *result = cast_out::cast(args.template call<Return>(((capture *) entry->data)->f), entry->policy, parent); detail::process_dynamic<Extra...>::precall(pyArgs); // call policy precall
detail::process_dynamic<Extra...>::postcall(pyArgs, result);
/* 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);
detail::process_dynamic<Extra...>::postcall(pyArgs, result); // call policy postcall
return result; return result;
}; };
/* Process any user-provided function annotations */
process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry); process_static(std::make_tuple(std::forward<Extra>(extra)...), m_entry);
/* Generate a readable signature describing the function's arguments and return value types */
PYBIND11_DESCR signature = cast_in::name() + detail::_(" -> ") + cast_out::name(); 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(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) { 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), function_entry *overloads = (function_entry *) PyCapsule_GetPointer(self, nullptr),
*it = overloads; *it = overloads;
/* Need to know how many arguments + keyword arguments there are to pick the right overload */
int nargs = (int) PyTuple_Size(args), int nargs = (int) PyTuple_Size(args),
nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0; nkwargs = kwargs ? (int) PyDict_Size(kwargs) : 0;
PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr, PyObject *parent = nargs > 0 ? PyTuple_GetItem(args, 0) : nullptr,
*result = (PyObject *) 1; *result = PYBIND11_TRY_NEXT_OVERLOAD;
try { try {
for (; it != nullptr; it = it->next) { for (; it != nullptr; it = it->next) {
object args_(args, true); object args_(args, true);
int kwargs_consumed = 0; 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()) { if (nargs < (int) it->args.size()) {
args_ = object(PyTuple_New(it->args.size()), false); args_ = object(PyTuple_New(it->args.size()), false);
for (int i = 0; i < nargs; ++i) { for (int i = 0; i < nargs; ++i) {
@ -292,7 +379,7 @@ private:
if (kwargs_consumed == nkwargs) if (kwargs_consumed == nkwargs)
result = it->impl(it, args_.ptr(), parent); result = it->impl(it, args_.ptr(), parent);
if (result != (PyObject *) 1) if (result != PYBIND11_TRY_NEXT_OVERLOAD)
break; break;
} }
} catch (const error_already_set &) { return nullptr; } catch (const error_already_set &) { return nullptr;
@ -309,7 +396,8 @@ private:
PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!"); PyErr_SetString(PyExc_RuntimeError, "Caught an unknown exception!");
return nullptr; return nullptr;
} }
if (result == (PyObject *) 1) {
if (result == PYBIND11_TRY_NEXT_OVERLOAD) {
std::string msg = "Incompatible function arguments. The " std::string msg = "Incompatible function arguments. The "
"following argument types are supported:\n"; "following argument types are supported:\n";
int ctr = 0; int ctr = 0;
@ -328,16 +416,17 @@ private:
return nullptr; return nullptr;
} else { } else {
if (overloads->is_constructor) { 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); PyObject *inst = PyTuple_GetItem(args, 0);
const detail::type_info *type_info = auto tinfo = detail::get_type_info(Py_TYPE(inst));
capsule(PyObject_GetAttrString((PyObject *) Py_TYPE(inst), tinfo->init_holder(inst, nullptr);
const_cast<char *>("__pybind11__")), false);
type_info->init_holder(inst, nullptr);
} }
return result; return result;
} }
} }
/// When a cpp_function is GCed, release any memory allocated by pybind11
static void destruct(function_entry *entry) { static void destruct(function_entry *entry) {
while (entry) { while (entry) {
function_entry *next = entry->next; function_entry *next = entry->next;
@ -352,7 +441,7 @@ private:
Py_XDECREF(arg.value); Py_XDECREF(arg.value);
} }
if (entry->def) { if (entry->def) {
free((char *) entry->def->ml_doc); std::free((char *) entry->def->ml_doc);
delete entry->def; delete entry->def;
} }
delete entry; delete entry;
@ -360,6 +449,7 @@ private:
} }
} }
/// 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(const char *text, const std::type_info * const * types, int args) {
/* Create copies of all referenced C-style strings */ /* Create copies of all referenced C-style strings */
m_entry->name = strdup(m_entry->name ? m_entry->name : ""); m_entry->name = strdup(m_entry->name ? m_entry->name : "");
@ -372,7 +462,7 @@ private:
else if (a.value) else if (a.value)
a.descr = strdup(((std::string) ((object) handle(a.value).attr("__repr__")).call().str()).c_str()); a.descr = strdup(((std::string) ((object) handle(a.value).attr("__repr__")).call().str()).c_str());
} }
auto const &registered_types = detail::get_internals().registered_types; auto const &registered_types = detail::get_internals().registered_types_cpp;
/* Generate a proper function signature */ /* Generate a proper function signature */
std::string signature; std::string signature;
@ -403,7 +493,7 @@ private:
throw std::runtime_error("Internal error while parsing type signature (1)"); throw std::runtime_error("Internal error while parsing type signature (1)");
auto it = registered_types.find(t); auto it = registered_types.find(t);
if (it != registered_types.end()) { if (it != registered_types.end()) {
signature += it->second.type->tp_name; signature += ((const detail::type_info *) it->second)->type->tp_name;
} else { } else {
std::string tname(t->name()); std::string tname(t->name());
detail::clean_type_id(tname); detail::clean_type_id(tname);
@ -423,7 +513,7 @@ private:
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
if (strcmp(m_entry->name, "__next__") == 0) { if (strcmp(m_entry->name, "__next__") == 0) {
free(m_entry->name); std::free(m_entry->name);
m_entry->name = strdup("next"); m_entry->name = strdup("next");
} }
#endif #endif
@ -447,21 +537,27 @@ private:
if (m_entry->sibling && PyCFunction_Check(m_entry->sibling)) { if (m_entry->sibling && PyCFunction_Check(m_entry->sibling)) {
capsule entry_capsule(PyCFunction_GetSelf(m_entry->sibling), true); capsule entry_capsule(PyCFunction_GetSelf(m_entry->sibling), true);
s_entry = (function_entry *) entry_capsule; s_entry = (function_entry *) entry_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_) if (s_entry->class_ != m_entry->class_)
s_entry = nullptr; /* Overridden method, don't append to parent class overloads */ s_entry = nullptr;
} }
if (!s_entry) { /* No existing overload was found, create a function object */ if (!s_entry) {
/* No existing overload was found, create a new function object */
m_entry->def = new PyMethodDef(); m_entry->def = new PyMethodDef();
memset(m_entry->def, 0, sizeof(PyMethodDef)); memset(m_entry->def, 0, sizeof(PyMethodDef));
m_entry->def->ml_name = m_entry->name; m_entry->def->ml_name = m_entry->name;
m_entry->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher); m_entry->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
m_entry->def->ml_flags = METH_VARARGS | METH_KEYWORDS; m_entry->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule entry_capsule(m_entry, [](PyObject *o) { destruct((function_entry *) PyCapsule_GetPointer(o, nullptr)); }); capsule entry_capsule(m_entry, [](PyObject *o) {
destruct((function_entry *) PyCapsule_GetPointer(o, nullptr));
});
m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr()); m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr());
if (!m_ptr) if (!m_ptr)
throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object"); throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object");
} else { } else {
/* Append at the end of the overload chain */
m_ptr = m_entry->sibling; m_ptr = m_entry->sibling;
inc_ref(); inc_ref();
entry = s_entry; entry = s_entry;
@ -473,7 +569,9 @@ private:
std::string signatures; std::string signatures;
int index = 0; int index = 0;
function_entry *it = entry; function_entry *it = entry;
while (it) { /* Create pydoc entry including all function signatures and docstrings of the overload chain */ /* Create a nice pydoc entry including all signatures and
docstrings of the functions in the overload chain */
while (it) {
if (s_entry) if (s_entry)
signatures += std::to_string(++index) + ". "; signatures += std::to_string(++index) + ". ";
signatures += "Signature : "; signatures += "Signature : ";
@ -490,7 +588,7 @@ private:
} }
PyCFunctionObject *func = (PyCFunctionObject *) m_ptr; PyCFunctionObject *func = (PyCFunctionObject *) m_ptr;
if (func->m_ml->ml_doc) if (func->m_ml->ml_doc)
free((char *) func->m_ml->ml_doc); std::free((char *) func->m_ml->ml_doc);
func->m_ml->ml_doc = strdup(signatures.c_str()); func->m_ml->ml_doc = strdup(signatures.c_str());
if (entry->class_) { if (entry->class_) {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
@ -505,6 +603,7 @@ private:
} }
}; };
/// Wrapper for Python extension modules
class module : public object { class module : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check) PYBIND11_OBJECT_DEFAULT(module, object, PyModule_Check)
@ -555,107 +654,122 @@ public:
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
/// Basic support for creating new Python heap types /// Basic support for creating new Python heap types
class custom_type : public object { class generic_type : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(custom_type, object, PyType_Check) PYBIND11_OBJECT_DEFAULT(generic_type, object, PyType_Check)
custom_type(object &scope, const char *name_, const std::type_info *tinfo, generic_type(const object &scope, const char *name_, const std::type_info *type_cpp,
size_t type_size, size_t instance_size, size_t type_size, size_t instance_size,
void (*init_holder)(PyObject *, const void *), const destructor &dealloc, void (*init_holder)(PyObject *, const void *),
PyObject *parent, const char *doc) { const destructor &dealloc, object parent, const char *doc) {
PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0);
#if PY_MAJOR_VERSION >= 3
PyObject *name = PyUnicode_FromString(name_);
#else
PyObject *name = PyString_FromString(name_);
#endif
if (type == nullptr || name == nullptr)
throw std::runtime_error("Internal error in custom_type::custom_type()");
Py_INCREF(name);
std::string full_name(name_);
pybind11::str scope_name = (object) scope.attr("__name__"), object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
module_name = (object) scope.attr("__module__"); object name(PYBIND11_FROM_STRING(name_), false);
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (scope_name.check()) if (!type_holder || !name)
full_name = std::string(scope_name) + "." + full_name; throw std::runtime_error("generic_type: unable to create type object!");
if (module_name.check())
full_name = std::string(module_name) + "." + full_name;
type->ht_name = name; /* Register supplemental type information in C++ dict */
#if PY_MAJOR_VERSION >= 3 auto &internals = get_internals();
type->ht_qualname = name; detail::type_info *tinfo = new detail::type_info();
#endif tinfo->type = (PyTypeObject *) type;
tinfo->type_size = type_size;
tinfo->init_holder = init_holder;
internals.registered_types_cpp[type_cpp] = tinfo;
internals.registered_types_py[type] = tinfo;
auto scope_module = (object) scope.attr("__module__");
if (!scope_module)
scope_module = (object) scope.attr("__name__");
std::string full_name = (scope_module ? ((std::string) scope_module.str() + "." + name_)
: std::string(name_));
/* Basic type attributes */
type->ht_type.tp_name = strdup(full_name.c_str()); type->ht_type.tp_name = strdup(full_name.c_str());
type->ht_type.tp_basicsize = instance_size; type->ht_type.tp_basicsize = instance_size;
type->ht_type.tp_init = (initproc) init; type->ht_type.tp_base = (PyTypeObject *) parent.release();
type->ht_type.tp_new = (newfunc) new_instance;
type->ht_type.tp_dealloc = dealloc; #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3
type->ht_type.tp_flags |= /* Qualified names for Python >= 3.3 */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; auto scope_qualname = (object) scope.attr("__qualname__");
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; if (scope_qualname) {
#if PY_MAJOR_VERSION < 3 type->ht_qualname = PyUnicode_FromFormat(
type->ht_type.tp_flags |= Py_TPFLAGS_CHECKTYPES; "%U.%U", scope_qualname.ptr(), name.ptr());
} else {
type->ht_qualname = name.ptr();
name.inc_ref();
}
#endif #endif
type->ht_name = name.release();
/* Supported protocols */
type->ht_type.tp_as_number = &type->as_number; type->ht_type.tp_as_number = &type->as_number;
type->ht_type.tp_as_sequence = &type->as_sequence; type->ht_type.tp_as_sequence = &type->as_sequence;
type->ht_type.tp_as_mapping = &type->as_mapping; type->ht_type.tp_as_mapping = &type->as_mapping;
type->ht_type.tp_base = (PyTypeObject *) parent;
/* Supported elementary operations */
type->ht_type.tp_init = (initproc) init;
type->ht_type.tp_new = (newfunc) new_instance;
type->ht_type.tp_dealloc = dealloc;
/* Support weak references (needed for the keep_alive feature) */
type->ht_type.tp_weaklistoffset = offsetof(instance<void>, weakrefs); type->ht_type.tp_weaklistoffset = offsetof(instance<void>, weakrefs);
/* Flags */
type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE;
#if PY_MAJOR_VERSION < 3
type->ht_type.tp_flags |= Py_TPFLAGS_CHECKTYPES;
#endif
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC;
if (doc) { if (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(doc) + 1;
type->ht_type.tp_doc = (char *) PyObject_MALLOC(size); 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, doc, size);
} }
Py_XINCREF(parent);
if (PyType_Ready(&type->ht_type) < 0) if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("Internal error in custom_type::custom_type()"); throw std::runtime_error("generic_type: PyType_Ready failed!");
m_ptr = (PyObject *) type;
/* Needed by pydoc */ m_ptr = type_holder.ptr();
attr("__module__") = scope_name;
auto &registered_types = get_internals().registered_types; if (scope_module) // Needed by pydoc
auto &type_info = registered_types[tinfo]; type_holder.attr("__module__") = scope_module;
type_info.type = (PyTypeObject *) m_ptr;
type_info.type_size = type_size;
type_info.init_holder = init_holder;
attr("__pybind11__") = capsule(&type_info);
scope.attr(name) = *this; /* Register type with the parent scope */
scope.attr(name_) = *this;
type_holder.release();
} }
protected: protected:
/* Allocate a metaclass on demand (for static properties) */ /// Allocate a metaclass on demand (for static properties)
handle metaclass() { handle metaclass() {
auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type; auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type;
#if PY_MAJOR_VERSION >= 3 auto &ob_type = PYBIND11_OB_TYPE(ht_type);
auto &ob_type = ht_type.ob_base.ob_base.ob_type;
#else
auto &ob_type = ht_type.ob_type;
#endif
if (ob_type == &PyType_Type) { if (ob_type == &PyType_Type) {
std::string name_ = std::string(ht_type.tp_name) + "_meta"; std::string name_ = std::string(ht_type.tp_name) + "__Meta";
PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0); object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
PyObject *name = PyUnicode_FromString(name_.c_str()); object name(PYBIND11_FROM_STRING(name_.c_str()), false);
if (type == nullptr || name == nullptr) if (!type_holder || !name)
throw std::runtime_error("Internal error in custom_type::metaclass()"); throw std::runtime_error("generic_type::metaclass(): unable to create type object!");
Py_INCREF(name);
type->ht_name = name; auto type = (PyHeapTypeObject*) type_holder.ptr();
#if PY_MAJOR_VERSION >= 3 type->ht_name = name.release();
type->ht_qualname = name;
#endif
type->ht_type.tp_name = strdup(name_.c_str()); type->ht_type.tp_name = strdup(name_.c_str());
type->ht_type.tp_base = &PyType_Type; type->ht_type.tp_base = &PyType_Type;
type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; type->ht_type.tp_flags |= (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE) &
type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; ~Py_TPFLAGS_HAVE_GC;
if (PyType_Ready(&type->ht_type) < 0) if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("Internal error in custom_type::metaclass()"); throw std::runtime_error("generic_type::metaclass(): PyType_Ready failed!");
ob_type = (PyTypeObject *) type;
Py_INCREF(type); ob_type = (PyTypeObject *) type_holder.release();
} }
return handle((PyObject *) ob_type); return handle((PyObject *) ob_type);
} }
@ -667,10 +781,9 @@ protected:
} }
static PyObject *new_instance(PyTypeObject *type, PyObject *, PyObject *) { static PyObject *new_instance(PyTypeObject *type, PyObject *, PyObject *) {
const detail::type_info *type_info = capsule( instance<void> *self = (instance<void> *) PyType_GenericAlloc((PyTypeObject *) type, 0);
PyObject_GetAttrString((PyObject *) type, const_cast<char*>("__pybind11__")), false); auto tinfo = detail::get_type_info(type);
instance<void> *self = (instance<void> *) PyType_GenericAlloc(type, 0); self->value = ::operator new(tinfo->type_size);
self->value = ::operator new(type_info->type_size);
self->owned = true; self->owned = true;
self->parent = nullptr; self->parent = nullptr;
self->constructed = false; self->constructed = false;
@ -685,7 +798,7 @@ protected:
auto &registered_instances = detail::get_internals().registered_instances; auto &registered_instances = detail::get_internals().registered_instances;
auto it = registered_instances.find(self->value); auto it = registered_instances.find(self->value);
if (it == registered_instances.end()) if (it == registered_instances.end())
throw std::runtime_error("Deallocating unregistered instance!"); throw std::runtime_error("generic_type::dealloc(): Tried to deallocate unregistered instance!");
registered_instances.erase(it); registered_instances.erase(it);
} }
Py_XDECREF(self->parent); Py_XDECREF(self->parent);
@ -705,20 +818,19 @@ protected:
#endif #endif
type->as_buffer.bf_getbuffer = getbuffer; type->as_buffer.bf_getbuffer = getbuffer;
type->as_buffer.bf_releasebuffer = releasebuffer; type->as_buffer.bf_releasebuffer = releasebuffer;
auto info = ((detail::type_info *) capsule(attr("__pybind11__"))); auto tinfo = detail::get_type_info(&type->ht_type);
info->get_buffer = get_buffer; tinfo->get_buffer = get_buffer;
info->get_buffer_data = get_buffer_data; tinfo->get_buffer_data = get_buffer_data;
} }
static int getbuffer(PyObject *obj, Py_buffer *view, int flags) { static int getbuffer(PyObject *obj, Py_buffer *view, int flags) {
auto const &typeinfo = ((detail::type_info *) capsule(handle(obj).attr("__pybind11__"))); auto tinfo = detail::get_type_info(Py_TYPE(obj));
if (view == nullptr || obj == nullptr || !tinfo || !tinfo->get_buffer) {
if (view == nullptr || obj == nullptr || !typeinfo || !typeinfo->get_buffer) { PyErr_SetString(PyExc_BufferError, "generic_type::getbuffer(): Internal error");
PyErr_SetString(PyExc_BufferError, "Internal error");
return -1; return -1;
} }
memset(view, 0, sizeof(Py_buffer)); memset(view, 0, sizeof(Py_buffer));
buffer_info *info = typeinfo->get_buffer(obj, typeinfo->get_buffer_data); buffer_info *info = tinfo->get_buffer(obj, tinfo->get_buffer_data);
view->obj = obj; view->obj = obj;
view->ndim = 1; view->ndim = 1;
view->internal = info; view->internal = info;
@ -749,22 +861,22 @@ template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined
template <typename... Args> struct init; template <typename... Args> struct init;
NAMESPACE_END(detail) NAMESPACE_END(detail)
template <typename type, typename holder_type = std::unique_ptr<type>> class class_ : public detail::custom_type { template <typename type, typename holder_type = std::unique_ptr<type>> class class_ : public detail::generic_type {
public: public:
typedef detail::instance<type, holder_type> instance_type; typedef detail::instance<type, holder_type> instance_type;
PYBIND11_OBJECT(class_, detail::custom_type, PyType_Check) PYBIND11_OBJECT(class_, detail::generic_type, PyType_Check)
class_(object &scope, const char *name, const char *doc = nullptr) class_(object &scope, const char *name, const char *doc = nullptr)
: detail::custom_type(scope, name, &typeid(type), sizeof(type), : detail::generic_type(scope, name, &typeid(type), sizeof(type),
sizeof(instance_type), init_holder, dealloc, sizeof(instance_type), init_holder, dealloc,
nullptr, doc) { } object(), doc) { }
class_(object &scope, const char *name, object &parent, class_(object &scope, const char *name, object &parent,
const char *doc = nullptr) const char *doc = nullptr)
: detail::custom_type(scope, name, &typeid(type), sizeof(type), : detail::generic_type(scope, name, &typeid(type), sizeof(type),
sizeof(instance_type), init_holder, dealloc, sizeof(instance_type), init_holder, dealloc,
parent.ptr(), doc) { } parent, doc) { }
template <typename Func, typename... Extra> template <typename Func, typename... Extra>
class_ &def(const char *name_, Func&& f, Extra&&... extra) { class_ &def(const char *name_, Func&& f, Extra&&... extra) {
@ -882,7 +994,7 @@ public:
} }
template <typename target> class_ alias() { template <typename target> class_ alias() {
auto &instances = pybind11::detail::get_internals().registered_types; auto &instances = pybind11::detail::get_internals().registered_types_cpp;
instances[&typeid(target)] = instances[&typeid(type)]; instances[&typeid(target)] = instances[&typeid(type)];
return *this; return *this;
} }
@ -929,7 +1041,7 @@ private:
else else
::operator delete(inst->value); ::operator delete(inst->value);
} }
custom_type::dealloc((detail::instance<void> *) inst); generic_type::dealloc((detail::instance<void> *) inst);
} }
}; };
@ -1023,11 +1135,11 @@ template <typename InputType, typename OutputType> void implicitly_convertible()
PyErr_Clear(); PyErr_Clear();
return result; return result;
}; };
auto & registered_types = detail::get_internals().registered_types; auto & registered_types = detail::get_internals().registered_types_cpp;
auto it = registered_types.find(&typeid(OutputType)); auto it = registered_types.find(&typeid(OutputType));
if (it == registered_types.end()) if (it == registered_types.end())
throw std::runtime_error("implicitly_convertible: Unable to find type " + type_id<OutputType>()); throw std::runtime_error("implicitly_convertible: Unable to find type " + type_id<OutputType>());
it->second.implicit_conversions.push_back(implicit_caster); ((detail::type_info *) it->second)->implicit_conversions.push_back(implicit_caster);
} }
inline void init_threading() { PyEval_InitThreads(); } inline void init_threading() { PyEval_InitThreads(); }

View File

@ -404,7 +404,6 @@ public:
class function : public object { class function : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check) PYBIND11_OBJECT_DEFAULT(function, object, PyFunction_Check)
bool is_cpp_function() const { bool is_cpp_function() const {
PyObject *ptr = detail::get_function(m_ptr); PyObject *ptr = detail::get_function(m_ptr);
return ptr != nullptr && PyCFunction_Check(ptr); return ptr != nullptr && PyCFunction_Check(ptr);
@ -426,7 +425,7 @@ public:
}; };
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
inline internals &get_internals() { PYBIND11_NOINLINE inline internals &get_internals() {
static internals *internals_ptr = nullptr; static internals *internals_ptr = nullptr;
if (internals_ptr) if (internals_ptr)
return *internals_ptr; return *internals_ptr;
@ -441,7 +440,19 @@ inline internals &get_internals() {
return *internals_ptr; return *internals_ptr;
} }
inline std::string error_string() { 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)
throw std::runtime_error("pybind11::detail::get_type_info: unable to find type object!");
} while (true);
}
PYBIND11_NOINLINE inline std::string error_string() {
std::string errorString; std::string errorString;
PyThreadState *tstate = PyThreadState_GET(); PyThreadState *tstate = PyThreadState_GET();
if (tstate == nullptr) if (tstate == nullptr)
@ -457,20 +468,12 @@ inline std::string error_string() {
return errorString; return errorString;
} }
inline handle get_object_handle(const void *ptr) { PYBIND11_NOINLINE inline handle get_object_handle(const void *ptr) {
auto instances = get_internals().registered_instances; auto instances = get_internals().registered_instances;
auto it = instances.find(ptr); auto it = instances.find(ptr);
if (it == instances.end()) if (it == instances.end())
return handle(); return handle();
return it->second; return handle((PyObject *) it->second);
}
inline handle get_type_handle(const std::type_info &tp) {
auto instances = get_internals().registered_types;
auto it = instances.find(&tp);
if (it == instances.end())
return handle();
return handle((PyObject *) it->second.type);
} }
NAMESPACE_END(detail) NAMESPACE_END(detail)