Commit 678d787c authored by Wenzel Jakob's avatar Wenzel Jakob
Browse files

do more work with classes from pytypes.h (especially for STL container casting)

parent d561cb01
......@@ -22,13 +22,13 @@ 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.
Tutorial and reference documentation is provided at
[http://pybind11.readthedocs.org/en/latest](http://pybind11.readthedocs.org/en/latest).
......
......@@ -19,13 +19,13 @@ 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.
Core features
*************
......
......@@ -10,7 +10,7 @@ test_function(enum=1)
None
test_function(enum=2)
None
<class 'Example4.EMode'>
<class 'example.EMode'>
EMode.EFirstMode
EMode.EFirstMode
Example4::test_function(enum=1)
......
......@@ -22,6 +22,8 @@ def sanitize(lines):
line = line.replace('__builtin__', 'builtins')
line = line.replace('example.', '')
line = line.replace('unicode', 'str')
line = line.replace('Example4.EMode', 'EMode')
line = line.replace('example.EMode', 'EMode')
line = line.replace('method of builtins.PyCapsule instance', '')
line = line.strip()
if sys.platform == 'win32':
......
......@@ -225,8 +225,8 @@ struct argument_entry {
/// 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
std::unordered_map<const void *, void*> registered_types_py; // PyTypeObject* -> type_info
std::unordered_map<const void *, void*> registered_instances; // void * -> PyObject*
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;
};
......@@ -271,4 +271,7 @@ struct error_already_set : public std::runtime_error { public: error_already_set
/// Thrown when pybind11::cast or handle::call fail due to a type casting error
struct cast_error : public std::runtime_error { public: cast_error(const std::string &w = "") : std::runtime_error(w) {} };
PYBIND11_NOINLINE inline void pybind11_fail(const char *reason) { throw std::runtime_error(reason); }
PYBIND11_NOINLINE inline void pybind11_fail(const std::string &reason) { throw std::runtime_error(reason); }
NAMESPACE_END(pybind11)
......@@ -24,7 +24,6 @@
#endif
#include "cast.h"
#include <iostream>
NAMESPACE_BEGIN(pybind11)
......@@ -196,9 +195,9 @@ protected:
a.value, return_value_policy::automatic, nullptr);
if (obj == nullptr)
throw std::runtime_error("arg(): could not convert default keyword "
"argument into a Python object (type not "
"registered yet?)");
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);
}
......@@ -490,7 +489,7 @@ protected:
} else if (c == '%') {
const std::type_info *t = types[type_index++];
if (!t)
throw std::runtime_error("Internal error while parsing type signature (1)");
pybind11_fail("Internal error while parsing type signature (1)");
auto it = registered_types.find(t);
if (it != registered_types.end()) {
signature += ((const detail::type_info *) it->second)->type->tp_name;
......@@ -504,7 +503,7 @@ protected:
}
}
if (type_depth != 0 || types[type_index] != nullptr)
throw std::runtime_error("Internal error while parsing type signature (2)");
pybind11_fail("Internal error while parsing type signature (2)");
#if !defined(PYBIND11_CPP14)
delete[] types;
......@@ -519,7 +518,7 @@ protected:
#endif
if (!m_entry->args.empty() && (int) m_entry->args.size() != args)
throw std::runtime_error(
pybind11_fail(
"cpp_function(): function \"" + std::string(m_entry->name) + "\" takes " +
std::to_string(args) + " arguments, but " + std::to_string(m_entry->args.size()) +
" pybind11::arg entries were specified!");
......@@ -555,7 +554,7 @@ protected:
});
m_ptr = PyCFunction_New(m_entry->def, entry_capsule.ptr());
if (!m_ptr)
throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object");
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;
......@@ -597,7 +596,7 @@ protected:
m_ptr = PyMethod_New(m_ptr, nullptr, entry->class_);
#endif
if (!m_ptr)
throw std::runtime_error("cpp_function::cpp_function(): Could not allocate instance method object");
pybind11_fail("cpp_function::cpp_function(): Could not allocate instance method object");
Py_DECREF(func);
}
}
......@@ -621,7 +620,7 @@ public:
m_ptr = Py_InitModule3(name, nullptr, doc);
#endif
if (m_ptr == nullptr)
throw std::runtime_error("Internal error in module::module()");
pybind11_fail("Internal error in module::module()");
inc_ref();
}
......@@ -647,7 +646,7 @@ public:
static module import(const char *name) {
PyObject *obj = PyImport_ImportModule(name);
if (!obj)
throw std::runtime_error("Module \"" + std::string(name) + "\" not found!");
pybind11_fail("Module \"" + std::string(name) + "\" not found!");
return module(obj, false);
}
};
......@@ -668,7 +667,7 @@ public:
auto type = (PyHeapTypeObject*) type_holder.ptr();
if (!type_holder || !name)
throw std::runtime_error("generic_type: unable to create type object!");
pybind11_fail("generic_type: unable to create type object!");
/* Register supplemental type information in C++ dict */
auto &internals = get_internals();
......@@ -732,7 +731,7 @@ public:
}
if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("generic_type: PyType_Ready failed!");
pybind11_fail("generic_type: PyType_Ready failed!");
m_ptr = type_holder.ptr();
......@@ -756,7 +755,7 @@ protected:
object type_holder(PyType_Type.tp_alloc(&PyType_Type, 0), false);
object name(PYBIND11_FROM_STRING(name_.c_str()), false);
if (!type_holder || !name)
throw std::runtime_error("generic_type::metaclass(): unable to create type object!");
pybind11_fail("generic_type::metaclass(): unable to create type object!");
auto type = (PyHeapTypeObject*) type_holder.ptr();
type->ht_name = name.release();
......@@ -767,7 +766,7 @@ protected:
~Py_TPFLAGS_HAVE_GC;
if (PyType_Ready(&type->ht_type) < 0)
throw std::runtime_error("generic_type::metaclass(): PyType_Ready failed!");
pybind11_fail("generic_type::metaclass(): PyType_Ready failed!");
ob_type = (PyTypeObject *) type_holder.release();
}
......@@ -798,7 +797,7 @@ protected:
auto &registered_instances = detail::get_internals().registered_instances;
auto it = registered_instances.find(self->value);
if (it == registered_instances.end())
throw std::runtime_error("generic_type::dealloc(): Tried to deallocate unregistered instance!");
pybind11_fail("generic_type::dealloc(): Tried to deallocate unregistered instance!");
registered_instances.erase(it);
}
Py_XDECREF(self->parent);
......@@ -1096,14 +1095,12 @@ PYBIND11_NOINLINE inline void keep_alive_impl(int Nurse, int Patient, PyObject *
handle patient(Patient > 0 ? PyTuple_GetItem(arg, Patient - 1) : ret);
if (!nurse || !patient)
throw std::runtime_error("Could not activate keep_alive!");
pybind11_fail("Could not activate keep_alive!");
cpp_function disable_lifesupport(
[patient](handle weakref) { patient.dec_ref(); weakref.dec_ref(); });
weakref wr(nurse, disable_lifesupport);
if (!wr)
throw std::runtime_error("Could not allocate weak reference!");
patient.inc_ref(); /* reference patient and leak the weak reference */
(void) wr.release();
......@@ -1138,7 +1135,7 @@ template <typename InputType, typename OutputType> void implicitly_convertible()
auto & registered_types = detail::get_internals().registered_types_cpp;
auto it = registered_types.find(&typeid(OutputType));
if (it == registered_types.end())
throw std::runtime_error("implicitly_convertible: Unable to find type " + type_id<OutputType>());
pybind11_fail("implicitly_convertible: Unable to find type " + type_id<OutputType>());
((detail::type_info *) it->second)->implicit_conversions.push_back(implicit_caster);
}
......@@ -1196,7 +1193,7 @@ inline function get_overload(const void *this_ptr, const char *name) {
#define PYBIND11_OVERLOAD_PURE(ret_type, class_name, name, ...) \
PYBIND11_OVERLOAD_INT(ret_type, class_name, name, __VA_ARGS__) \
throw std::runtime_error("Tried to call pure virtual function \"" #name "\"");
pybind11::pybind11_fail("Tried to call pure virtual function \"" #name "\"");
NAMESPACE_END(pybind11)
......
......@@ -88,12 +88,12 @@ public:
iterator(PyObject *obj, bool borrowed = false) : object(obj, borrowed) { ++*this; }
iterator& operator++() {
if (ptr())
value = object(PyIter_Next(ptr()), false);
value = object(PyIter_Next(m_ptr), false);
return *this;
}
bool operator==(const iterator &it) const { return *it == **this; }
bool operator!=(const iterator &it) const { return *it != **this; }
const object &operator*() const { return value; }
const handle &operator*() const { return value; }
bool check() const { return PyIter_Check(ptr()); }
private:
object value;
......@@ -127,10 +127,10 @@ public:
void operator=(const handle &h) {
if (attr) {
if (PyObject_SetAttr(obj, key, (PyObject *) h.ptr()) < 0)
throw std::runtime_error("Unable to set object attribute");
pybind11_fail("Unable to set object attribute");
} else {
if (PyObject_SetItem(obj, key, (PyObject *) h.ptr()) < 0)
throw std::runtime_error("Unable to set object item");
pybind11_fail("Unable to set object item");
}
}
......@@ -164,12 +164,12 @@ public:
void operator=(const handle &o) {
o.inc_ref(); // PyList_SetItem steals a reference
if (PyList_SetItem(list, (ssize_t) index, (PyObject *) o.ptr()) < 0)
throw std::runtime_error("Unable to assign value in Python list!");
pybind11_fail("Unable to assign value in Python list!");
}
operator object() const {
PyObject *result = PyList_GetItem(list, (ssize_t) index);
if (!result)
throw std::runtime_error("Unable to retrieve value from Python list!");
pybind11_fail("Unable to retrieve value from Python list!");
return object(result, true);
}
private:
......@@ -184,12 +184,12 @@ public:
void operator=(const handle &o) {
o.inc_ref(); // PyTuple_SetItem steals a reference
if (PyTuple_SetItem(tuple, (ssize_t) index, (PyObject *) o.ptr()) < 0)
throw std::runtime_error("Unable to assign value in Python tuple!");
pybind11_fail("Unable to assign value in Python tuple!");
}
operator object() const {
PyObject *result = PyTuple_GetItem(tuple, (ssize_t) index);
if (!result)
throw std::runtime_error("Unable to retrieve value from Python tuple!");
pybind11_fail("Unable to retrieve value from Python tuple!");
return object(result, true);
}
private:
......@@ -205,8 +205,8 @@ public:
pos = -1;
return *this;
}
std::pair<object, object> operator*() const {
return std::make_pair(object(key, true), object(value, true));
std::pair<handle, handle> operator*() const {
return std::make_pair(key, value);
}
bool operator==(const dict_iterator &it) const { return it.pos == pos; }
bool operator!=(const dict_iterator &it) const { return it.pos != pos; }
......@@ -242,7 +242,10 @@ inline iterator handle::end() const { return iterator(nullptr); }
class str : public object {
public:
PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check)
str(const std::string &s) : object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) { }
str(const std::string &s)
: object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) {
if (!m_ptr) pybind11_fail("Could not allocate string object!");
}
operator std::string() const {
#if PY_MAJOR_VERSION >= 3
......@@ -250,7 +253,7 @@ public:
#else
object temp(PyUnicode_AsUTF8String(m_ptr), false);
if (temp.ptr() == nullptr)
throw std::runtime_error("Unable to extract string contents!");
pybind11_fail("Unable to extract string contents!");
return PyString_AsString(temp.ptr());
#endif
}
......@@ -270,14 +273,16 @@ public:
PYBIND11_OBJECT_DEFAULT(bytes, object, PYBIND11_BYTES_CHECK)
bytes(const std::string &s)
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), s.size()), false) { }
: object(PYBIND11_BYTES_FROM_STRING_AND_SIZE(s.data(), s.size()), false) {
if (!m_ptr) pybind11_fail("Could not allocate bytes object!");
}
operator std::string() const {
char *buffer;
ssize_t length;
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(m_ptr, &buffer, &length);
if (err == -1)
throw std::runtime_error("Unable to extract bytes contents!");
pybind11_fail("Unable to extract bytes contents!");
return std::string(buffer, length);
}
};
......@@ -306,6 +311,7 @@ public:
else
m_ptr = PyLong_FromUnsignedLongLong((unsigned long long) value);
}
if (!m_ptr) pybind11_fail("Could not allocate int object!");
}
template <typename T,
......@@ -328,8 +334,12 @@ public:
class float_ : public object {
public:
PYBIND11_OBJECT_DEFAULT(float_, object, PyFloat_Check)
float_(float value) : object(PyFloat_FromDouble((double) value), false) { }
float_(double value) : object(PyFloat_FromDouble((double) value), false) { }
float_(float value) : object(PyFloat_FromDouble((double) value), false) {
if (!m_ptr) pybind11_fail("Could not allocate float object!");
}
float_(double value) : object(PyFloat_FromDouble((double) value), false) {
if (!m_ptr) pybind11_fail("Could not allocate float object!");
}
operator float() const { return (float) PyFloat_AsDouble(m_ptr); }
operator double() const { return (double) PyFloat_AsDouble(m_ptr); }
};
......@@ -337,7 +347,9 @@ public:
class weakref : public object {
public:
PYBIND11_OBJECT_DEFAULT(weakref, object, PyWeakref_Check)
weakref(handle obj, handle callback = handle()) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), false) { }
weakref(handle obj, handle callback = handle()) : object(PyWeakref_NewRef(obj.ptr(), callback.ptr()), false) {
if (!m_ptr) pybind11_fail("Could not allocate weak reference!");
}
};
class slice : public object {
......@@ -346,6 +358,7 @@ public:
slice(ssize_t start_, ssize_t stop_, ssize_t step_) {
int_ start(start_), stop(stop_), step(step_);
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr());
if (!m_ptr) pybind11_fail("Could not allocate slice object!");
}
bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const {
return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, length,
......@@ -357,10 +370,13 @@ class capsule : public object {
public:
PYBIND11_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
capsule(PyObject *obj, bool borrowed) : object(obj, borrowed) { }
capsule(void *value, void (*destruct)(PyObject *) = nullptr) : object(PyCapsule_New(value, nullptr, destruct), false) { }
capsule(void *value, void (*destruct)(PyObject *) = nullptr)
: object(PyCapsule_New(value, nullptr, destruct), false) {
if (!m_ptr) pybind11_fail("Could not allocate capsule object!");
}
template <typename T> operator T *() const {
T * result = static_cast<T *>(PyCapsule_GetPointer(m_ptr, nullptr));
if (!result) throw std::runtime_error("Unable to extract capsule contents!");
if (!result) pybind11_fail("Unable to extract capsule contents!");
return result;
}
};
......@@ -368,7 +384,9 @@ public:
class tuple : public object {
public:
PYBIND11_OBJECT(tuple, object, PyTuple_Check)
tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) { }
tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) {
if (!m_ptr) pybind11_fail("Could not allocate tuple object!");
}
size_t size() const { return (size_t) PyTuple_Size(m_ptr); }
detail::tuple_accessor operator[](size_t index) const { return detail::tuple_accessor(ptr(), index); }
};
......@@ -376,7 +394,9 @@ public:
class dict : public object {
public:
PYBIND11_OBJECT(dict, object, PyDict_Check)
dict() : object(PyDict_New(), false) { }
dict() : object(PyDict_New(), false) {
if (!m_ptr) pybind11_fail("Could not allocate dict object!");
}
size_t size() const { return (size_t) PyDict_Size(m_ptr); }
detail::dict_iterator begin() const { return (++detail::dict_iterator(ptr(), 0)); }
detail::dict_iterator end() const { return detail::dict_iterator(); }
......@@ -386,7 +406,9 @@ public:
class list : public object {
public:
PYBIND11_OBJECT(list, object, PyList_Check)
list(size_t size = 0) : object(PyList_New((ssize_t) size), false) { }
list(size_t size = 0) : object(PyList_New((ssize_t) size), false) {
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()); }
......@@ -395,10 +417,12 @@ public:
class set : public object {
public:
PYBIND11_OBJECT(set, object, PySet_Check)
set() : object(PySet_New(nullptr), false) { }
set() : object(PySet_New(nullptr), false) {
if (!m_ptr) pybind11_fail("Could not allocate set object!");
}
size_t size() const { return (size_t) PySet_Size(m_ptr); }
void add(const object &object) const { PySet_Add(m_ptr, (PyObject *) object.ptr()); }
void clear() const { PySet_Clear(ptr()); }
bool add(const object &object) const { return PySet_Add(m_ptr, (PyObject *) object.ptr()) == 0; }
void clear() const { PySet_Clear(m_ptr); }
};
class function : public object {
......@@ -448,7 +472,7 @@ PYBIND11_NOINLINE inline detail::type_info* get_type_info(PyTypeObject *type) {
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!");
pybind11_fail("pybind11::detail::get_type_info: unable to find type object!");
} while (true);
}
......
......@@ -14,7 +14,6 @@
#include <set>
#include <iostream>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
......@@ -28,14 +27,14 @@ template <typename Value, typename Alloc> struct type_caster<std::vector<Value,
typedef type_caster<Value> value_conv;
public:
bool load(PyObject *src, bool convert) {
if (!PyList_Check(src))
list l(src, true);
if (!l.check())
return false;
size_t size = (size_t) PyList_GET_SIZE(src);
value.reserve(size);
value.reserve(l.size());
value.clear();
value_conv conv;
for (size_t i=0; i<size; ++i) {
if (!conv.load(PyList_GetItem(src, (ssize_t) i), convert))
for (auto it : l) {
if (!conv.load(it.ptr(), convert))
return false;
value.push_back((Value) conv);
}
......@@ -43,17 +42,15 @@ public:
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
object list(PyList_New(src.size()), false);
if (!list)
return nullptr;
list l(src.size());
size_t index = 0;
for (auto const &value: src) {
object value_ (value_conv::cast(value, policy, parent), false);
object value_(value_conv::cast(value, policy, parent), false);
if (!value_)
return nullptr;
PyList_SET_ITEM(list.ptr(), index++, value_.release()); // steals a reference
PyList_SET_ITEM(l.ptr(), index++, value_.release()); // steals a reference
}
return list.release();
return l.release();
}
PYBIND11_TYPE_CASTER(type, _("list<") + value_conv::name() + _(">"));
};
......@@ -68,8 +65,8 @@ public:
return false;
value.clear();
key_conv conv;
for (const object &o: s) {
if (!conv.load((PyObject *) o.ptr(), convert))
for (auto entry : s) {
if (!conv.load(entry.ptr(), convert))
return false;
value.insert((Key) conv);
}
......@@ -77,15 +74,13 @@ public:
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
object set(PySet_New(nullptr), false);
if (!set)
return nullptr;
pybind11::set s;
for (auto const &value: src) {
object value_(key_conv::cast(value, policy, parent), false);
if (!value_ || PySet_Add(set.ptr(), value_.ptr()) != 0)
if (!value_ || !s.add(value))
return nullptr;
}
return set.release();
return s.release();
}
PYBIND11_TYPE_CASTER(type, _("set<") + key_conv::name() + _(">"));
};
......@@ -97,16 +92,15 @@ public:
typedef type_caster<Value> value_conv;
bool load(PyObject *src, bool convert) {
if (!PyDict_Check(src))
dict d(src, true);
if (!d.check())
return false;
value.clear();
PyObject *key_, *value_;
ssize_t pos = 0;
key_conv kconv;
value_conv vconv;
while (PyDict_Next(src, &pos, &key_, &value_)) {
if (!kconv.load(key_, convert) || !vconv.load(value_, convert))
value.clear();
for (auto it : d) {
if (!kconv.load(it.first.ptr(), convert) ||
!vconv.load(it.second.ptr(), convert))
return false;
value[(Key) kconv] = (Value) vconv;
}
......@@ -114,16 +108,15 @@ public:
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
object dict(PyDict_New(), false);
if (!dict)
return nullptr;
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);
if (!key || !value || PyDict_SetItem(dict.ptr(), key.ptr(), value.ptr()) != 0)
if (!key || !value)
return nullptr;
d[key] = value;
}
return dict.release();
return d.release();
}
PYBIND11_TYPE_CASTER(type, _("dict<") + key_conv::name() + _(", ") + value_conv::name() + _(">"));
......@@ -131,7 +124,10 @@ public:
NAMESPACE_END(detail)
inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (std::string) obj.str(); return os; }
inline std::ostream &operator<<(std::ostream &os, const handle &obj) {
os << (std::string) obj.str();
return os;
}
NAMESPACE_END(pybind11)
......
......@@ -11,6 +11,7 @@
#include <cstdio>
#include <cstdlib>
#if defined(__GNUG__)
#include <cxxabi.h>
#endif
......@@ -26,7 +27,7 @@ inline void erase_all(std::string &string, const std::string &search) {
}
}
inline void clean_type_id(std::string &name) {
PYBIND11_NOINLINE inline void clean_type_id(std::string &name) {