diff --git a/CMakeLists.txt b/CMakeLists.txt index 71796165..2e80bd01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) endif() set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6) -find_package(PythonLibs 3 REQUIRED) -find_package(PythonInterp 3 REQUIRED) +find_package(PythonLibs REQUIRED) +find_package(PythonInterp REQUIRED) string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) if (UNIX) diff --git a/README.md b/README.md index 36e81585..ead5b37d 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ 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 whole -codebase requires just over 2000 lines of code and only depends on Python and -the C++ standard library. This compact implementation was possible thanks to -some of the new C++11 language features (tuples, lambda functions and variadic -templates), and by only targeting Python 3.x and higher. +codebase requires less than 3000 lines of code and only depends 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 (tuples, lambda functions and +variadic templates). ## Core features The following core C++ features can be mapped to Python diff --git a/example/example1.py b/example/example1.py index 30cda8d3..f89b662d 100755 --- a/example/example1.py +++ b/example/example1.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example10.py b/example/example10.py index 401c5ccc..96cf9b07 100755 --- a/example/example10.py +++ b/example/example10.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example11.py b/example/example11.py index 733f6deb..7fc1b481 100755 --- a/example/example11.py +++ b/example/example11.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys, pydoc sys.path.append('.') diff --git a/example/example2.py b/example/example2.py index b0f47079..2782da54 100755 --- a/example/example2.py +++ b/example/example2.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys, pydoc sys.path.append('.') diff --git a/example/example3.py b/example/example3.py index a8893205..41cf3144 100755 --- a/example/example3.py +++ b/example/example3.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example4.py b/example/example4.py index aa2a448a..a926c142 100755 --- a/example/example4.py +++ b/example/example4.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example5.py b/example/example5.py index 7e0dfd01..4e75e171 100755 --- a/example/example5.py +++ b/example/example5.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example6.py b/example/example6.py index 7dbadc4b..5a014dda 100755 --- a/example/example6.py +++ b/example/example6.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example7.py b/example/example7.py index 565ceac6..3bfddb03 100755 --- a/example/example7.py +++ b/example/example7.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example8.py b/example/example8.py index 9c295d29..e918a77f 100755 --- a/example/example8.py +++ b/example/example8.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/example/example9.py b/example/example9.py index d8c654eb..9a29353c 100755 --- a/example/example9.py +++ b/example/example9.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +from __future__ import print_function import sys sys.path.append('.') diff --git a/include/pybind/cast.h b/include/pybind/cast.h index a46f9fb9..2feaf84e 100644 --- a/include/pybind/cast.h +++ b/include/pybind/cast.h @@ -25,7 +25,7 @@ NAMESPACE_BEGIN(detail) #endif /** Linked list descriptor type for function signatures (produces smaller binaries - * compared to a previous solution using std::string and operator +=) */ + compared to a previous solution using std::string and operator +=) */ class descr { public: struct entry { @@ -241,18 +241,42 @@ protected: PYBIND_TYPE_CASTER(type, #type); \ }; +#if PY_MAJOR_VERSION >= 3 +#define PyLong_AsUnsignedLongLong_Fixed PyLong_AsUnsignedLongLong +#define PyLong_AsLongLong_Fixed PyLong_AsLongLong +#else +inline PY_LONG_LONG PyLong_AsLongLong_Fixed(PyObject *o) { + if (PyInt_Check(o)) + return (PY_LONG_LONG) PyLong_AsLong(o); + else + return ::PyLong_AsLongLong(o); +} + +inline unsigned PY_LONG_LONG PyLong_AsUnsignedLongLong_Fixed(PyObject *o) { + if (PyInt_Check(o)) + return (unsigned PY_LONG_LONG) PyLong_AsUnsignedLong(o); + else + return ::PyLong_AsUnsignedLongLong(o); +} +#endif + PYBIND_TYPE_CASTER_NUMBER(int8_t, long, PyLong_AsLong, PyLong_FromLong) PYBIND_TYPE_CASTER_NUMBER(uint8_t, unsigned long, PyLong_AsUnsignedLong, PyLong_FromUnsignedLong) PYBIND_TYPE_CASTER_NUMBER(int16_t, long, PyLong_AsLong, PyLong_FromLong) PYBIND_TYPE_CASTER_NUMBER(uint16_t, unsigned long, PyLong_AsUnsignedLong, PyLong_FromUnsignedLong) PYBIND_TYPE_CASTER_NUMBER(int32_t, long, PyLong_AsLong, PyLong_FromLong) PYBIND_TYPE_CASTER_NUMBER(uint32_t, unsigned long, PyLong_AsUnsignedLong, PyLong_FromUnsignedLong) -PYBIND_TYPE_CASTER_NUMBER(int64_t, PY_LONG_LONG, PyLong_AsLongLong, PyLong_FromLongLong) -PYBIND_TYPE_CASTER_NUMBER(uint64_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong, PyLong_FromUnsignedLongLong) +PYBIND_TYPE_CASTER_NUMBER(int64_t, PY_LONG_LONG, PyLong_AsLongLong_Fixed, PyLong_FromLongLong) +PYBIND_TYPE_CASTER_NUMBER(uint64_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong_Fixed, PyLong_FromUnsignedLongLong) #if defined(__APPLE__) // size_t/ssize_t are separate types on Mac OS X +#if PY_MAJOR_VERSION >= 3 PYBIND_TYPE_CASTER_NUMBER(ssize_t, Py_ssize_t, PyLong_AsSsize_t, PyLong_FromSsize_t) PYBIND_TYPE_CASTER_NUMBER(size_t, size_t, PyLong_AsSize_t, PyLong_FromSize_t) +#else +PYBIND_TYPE_CASTER_NUMBER(ssize_t, PY_LONG_LONG, PyLong_AsLongLong_Fixed, PyLong_FromLongLong) +PYBIND_TYPE_CASTER_NUMBER(size_t, unsigned PY_LONG_LONG, PyLong_AsUnsignedLongLong_Fixed, PyLong_FromUnsignedLongLong) +#endif #endif PYBIND_TYPE_CASTER_NUMBER(float, double, PyFloat_AsDouble, PyFloat_FromDouble) @@ -286,7 +310,19 @@ public: template <> class type_caster { public: bool load(PyObject *src, bool) { +#if PY_MAJOR_VERSION >= 3 const char *ptr = PyUnicode_AsUTF8(src); +#else + const char *ptr = nullptr; + object temp; + if (PyString_Check(src)) { + ptr = PyString_AsString(src); + } else { + temp = object(PyUnicode_AsUTF8String(src), false); + if (temp.ptr() != nullptr) + ptr = PyString_AsString(temp.ptr()); + } +#endif if (!ptr) { PyErr_Clear(); return false; } value = std::string(ptr); return true; @@ -301,13 +337,25 @@ public: template <> class type_caster { public: bool load(PyObject *src, bool) { +#if PY_MAJOR_VERSION >= 3 const wchar_t *ptr = PyUnicode_AsWideCharString(src, nullptr); +#else + object temp(PyUnicode_AsUTF16String(src), false); + if (temp.ptr() == nullptr) + return false; + const wchar_t *ptr = (wchar_t*) PyString_AsString(temp.ptr()); +#endif + if (!ptr) { PyErr_Clear(); return false; } value = std::wstring(ptr); return true; } static PyObject *cast(const std::wstring &src, return_value_policy /* policy */, PyObject * /* parent */) { +#if PY_MAJOR_VERSION >= 3 return PyUnicode_FromWideChar(src.c_str(), src.length()); +#else + return PyUnicode_DecodeUTF16((const char *) src.c_str(), src.length() * 2, "strict", nullptr); +#endif } PYBIND_TYPE_CASTER(std::wstring, "wstr"); }; @@ -316,7 +364,14 @@ public: template <> class type_caster { public: bool load(PyObject *src, bool) { +#if PY_MAJOR_VERSION >= 3 char *ptr = PyUnicode_AsUTF8(src); +#else + temp = object(PyUnicode_AsLatin1String(src), false); + if (temp.ptr() == nullptr) + return false; + char *ptr = PyString_AsString(temp.ptr()); +#endif if (!ptr) { PyErr_Clear(); return false; } value = ptr; return true; @@ -337,6 +392,9 @@ public: operator char() { return *value; } protected: char *value; +#if PY_MAJOR_VERSION < 3 + object temp; +#endif }; template class type_caster> { diff --git a/include/pybind/common.h b/include/pybind/common.h index 8a9850dd..d327ab6a 100644 --- a/include/pybind/common.h +++ b/include/pybind/common.h @@ -24,9 +24,6 @@ #endif #endif -#define PYTHON_PLUGIN(name) \ - extern "C" PYTHON_EXPORT PyObject *PyInit_##name() - #include #include #include @@ -37,7 +34,7 @@ #if defined(_MSC_VER) #define HAVE_ROUND #pragma warning(push) -#pragma warning(disable: 4510 4610 4512) +#pragma warning(disable: 4510 4610 4512 4005) #if _DEBUG #define _DEBUG_MARKER #undef _DEBUG @@ -61,6 +58,14 @@ #pragma warning(pop) #endif +#if PY_MAJOR_VERSION >= 3 +#define PYTHON_PLUGIN(name) \ + extern "C" PYTHON_EXPORT PyObject *PyInit_##name() +#else +#define PYTHON_PLUGIN(name) \ + extern "C" PYTHON_EXPORT PyObject *init##name() +#endif + NAMESPACE_BEGIN(pybind) typedef Py_ssize_t ssize_t; diff --git a/include/pybind/numpy.h b/include/pybind/numpy.h index d101013b..30428003 100644 --- a/include/pybind/numpy.h +++ b/include/pybind/numpy.h @@ -47,7 +47,11 @@ public: static API lookup() { PyObject *numpy = PyImport_ImportModule("numpy.core.multiarray"); PyObject *capsule = numpy ? PyObject_GetAttrString(numpy, "_ARRAY_API") : nullptr; +#if PY_MAJOR_VERSION >= 3 void **api_ptr = (void **) (capsule ? PyCapsule_GetPointer(capsule, NULL) : nullptr); +#else + void **api_ptr = (void **) (capsule ? PyCObject_AsVoidPtr(capsule) : nullptr); +#endif Py_XDECREF(capsule); Py_XDECREF(numpy); if (api_ptr == nullptr) diff --git a/include/pybind/operators.h b/include/pybind/operators.h index e0f858ff..84fc4c25 100644 --- a/include/pybind/operators.h +++ b/include/pybind/operators.h @@ -103,7 +103,11 @@ inline op_ op(const self_t &) { PYBIND_BINARY_OPERATOR(sub, rsub, operator-, l - r) PYBIND_BINARY_OPERATOR(add, radd, operator+, l + r) PYBIND_BINARY_OPERATOR(mul, rmul, operator*, l * r) +#if PY_MAJOR_VERSION >= 3 PYBIND_BINARY_OPERATOR(truediv, rtruediv, operator/, l / r) +#else +PYBIND_BINARY_OPERATOR(div, rdiv, operator/, l / r) +#endif PYBIND_BINARY_OPERATOR(mod, rmod, operator%, l % r) PYBIND_BINARY_OPERATOR(lshift, rlshift, operator<<, l << r) PYBIND_BINARY_OPERATOR(rshift, rrshift, operator>>, l >> r) diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h index fa2cffd0..45ce9e4c 100644 --- a/include/pybind/pybind.h +++ b/include/pybind/pybind.h @@ -45,7 +45,14 @@ template struct arg_t : public arg { template inline arg_t arg::operator=(const T &value) { return arg_t(name, value); } /// Annotation for methods -struct is_method { }; +struct is_method { +#if PY_MAJOR_VERSION < 3 + PyObject *class_; + is_method(object *o) : class_(o->ptr()) { } +#else + is_method(object *) { } +#endif +}; /// Annotation for documentation struct doc { const char *value; doc(const char *value) : value(value) { } }; @@ -69,11 +76,16 @@ private: short keywords = 0; return_value_policy policy = return_value_policy::automatic; std::string signature; +#if PY_MAJOR_VERSION < 3 + PyObject *class_ = nullptr; +#endif PyObject *sibling = nullptr; const char *doc = nullptr; function_entry *next = nullptr; }; + function_entry *m_entry; + /// Picks a suitable return value converter from cast.h template using return_value_caster = detail::type_casterkeywords++] = strdup(std::to_string(a.value).c_str()); } - static void process_extra(const pybind::is_method &, function_entry *entry, const char **, const char **) { entry->is_method = true; } + static void process_extra(const pybind::is_method &m, function_entry *entry, const char **, const char **) { + entry->is_method = true; +#if PY_MAJOR_VERSION < 3 + entry->class_ = m.class_; +#else + (void) m; +#endif + } static void process_extra(const pybind::return_value_policy p, function_entry *entry, const char **, const char **) { entry->policy = p; } static void process_extra(pybind::sibling s, function_entry *entry, const char **, const char **) { entry->sibling = s.value; } @@ -170,13 +189,13 @@ public: std::tuple extras; }; - function_entry *entry = new function_entry(); - entry->data = new capture { f, std::tuple(std::forward(extra)...) }; + m_entry = new function_entry(); + m_entry->data = new capture { f, std::tuple(std::forward(extra)...) }; typedef arg_value_caster cast_in; typedef return_value_caster cast_out; - entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * { + m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject * { capture *data = (capture *) entry->data; process_extras(data->extras, pyArgs, kwargs, entry->is_method); cast_in args; @@ -187,14 +206,14 @@ public: const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg); std::array kw{}, def{}; - process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data()); + process_extras(((capture *) m_entry->data)->extras, m_entry, kw.data(), def.data()); detail::descr d = cast_in::name(kw.data(), def.data()); d += " -> "; d += std::move(cast_out::name()); - initialize(entry, d, sizeof...(Arg)); + initialize(d, sizeof...(Arg)); } /// Delegating helper constructor to deal with lambda functions @@ -219,6 +238,9 @@ public: (Return (*)(const Class *, Arg ...)) nullptr, std::forward(extra)...); } + /// Return the function name + const char *name() const { return m_entry->name; } + private: /// Functors, lambda functions, etc. template @@ -228,13 +250,13 @@ private: std::tuple extras; }; - function_entry *entry = new function_entry(); - entry->data = new capture { std::forward(f), std::tuple(std::forward(extra)...) }; + m_entry = new function_entry(); + m_entry->data = new capture { std::forward(f), std::tuple(std::forward(extra)...) }; typedef arg_value_caster cast_in; typedef return_value_caster cast_out; - entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{ + m_entry->impl = [](function_entry *entry, PyObject *pyArgs, PyObject *kwargs, PyObject *parent) -> PyObject *{ capture *data = (capture *)entry->data; process_extras(data->extras, pyArgs, kwargs, entry->is_method); cast_in args; @@ -245,13 +267,13 @@ private: const int N = sizeof...(Extra) > sizeof...(Arg) ? sizeof...(Extra) : sizeof...(Arg); std::array kw{}, def{}; - process_extras(((capture *) entry->data)->extras, entry, kw.data(), def.data()); + process_extras(((capture *) m_entry->data)->extras, m_entry, kw.data(), def.data()); detail::descr d = cast_in::name(kw.data(), def.data()); d += " -> "; d += std::move(cast_out::name()); - initialize(entry, d, sizeof...(Arg)); + initialize(d, sizeof...(Arg)); } static PyObject *dispatcher(PyObject *self, PyObject *args, PyObject *kwargs ) { @@ -322,19 +344,31 @@ private: } } - void initialize(function_entry *entry, const detail::descr &descr, int args) { - if (entry->name == nullptr) - entry->name = ""; + void initialize(const detail::descr &descr, int args) { + if (m_entry->name == nullptr) + m_entry->name = ""; - if (entry->keywords != 0 && entry->keywords != args) +#if PY_MAJOR_VERSION < 3 + if (strcmp(m_entry->name, "__next__") == 0) + m_entry->name = "next"; +#endif + + if (m_entry->keywords != 0 && m_entry->keywords != args) throw std::runtime_error( - "cpp_function(): function \"" + std::string(entry->name) + "\" takes " + - std::to_string(args) + " arguments, but " + std::to_string(entry->keywords) + + "cpp_function(): function \"" + std::string(m_entry->name) + "\" takes " + + std::to_string(args) + " arguments, but " + std::to_string(m_entry->keywords) + " pybind::arg entries were specified!"); - entry->is_constructor = !strcmp(entry->name, "__init__"); - entry->signature = descr.str(); + m_entry->is_constructor = !strcmp(m_entry->name, "__init__"); + m_entry->signature = descr.str(); +#if PY_MAJOR_VERSION < 3 + if (m_entry->sibling && PyMethod_Check(m_entry->sibling)) + m_entry->sibling = PyMethod_GET_FUNCTION(m_entry->sibling); +#endif + + function_entry *entry = m_entry; + bool overloaded = false; if (!entry->sibling || !PyCFunction_Check(entry->sibling)) { entry->def = new PyMethodDef(); memset(entry->def, 0, sizeof(PyMethodDef)); @@ -354,13 +388,14 @@ private: parent = parent->next; parent->next = entry; entry = backup; + overloaded = true; } std::string signatures; int index = 0; function_entry *it = entry; while (it) { /* Create pydoc it */ - if (it->sibling) + if (overloaded) signatures += std::to_string(++index) + ". "; signatures += "Signature : " + std::string(it->signature) + "\n"; if (it->doc && strlen(it->doc) > 0) @@ -374,7 +409,11 @@ private: std::free((char *) func->m_ml->ml_doc); func->m_ml->ml_doc = strdup(signatures.c_str()); if (entry->is_method) { +#if PY_MAJOR_VERSION >= 3 m_ptr = PyInstanceMethod_New(m_ptr); +#else + 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"); Py_DECREF(func); @@ -387,6 +426,7 @@ public: PYBIND_OBJECT_DEFAULT(module, object, PyModule_Check) module(const char *name, const char *doc = nullptr) { +#if PY_MAJOR_VERSION >= 3 PyModuleDef *def = new PyModuleDef(); memset(def, 0, sizeof(PyModuleDef)); def->m_name = name; @@ -394,6 +434,9 @@ public: def->m_size = -1; Py_INCREF(def); m_ptr = PyModule_Create(def); +#else + m_ptr = Py_InitModule3(name, nullptr, doc); +#endif if (m_ptr == nullptr) throw std::runtime_error("Internal error in module::module()"); inc_ref(); @@ -430,7 +473,11 @@ public: void (*init_holder)(PyObject *), const destructor &dealloc, PyObject *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); @@ -444,7 +491,10 @@ public: if (module_name.check()) full_name = std::string(module_name) + "." + full_name; - type->ht_name = type->ht_qualname = name; + type->ht_name = name; +#if PY_MAJOR_VERSION >= 3 + type->ht_qualname = name; +#endif type->ht_type.tp_name = strdup(full_name.c_str()); type->ht_type.tp_basicsize = instance_size; type->ht_type.tp_init = (initproc) init; @@ -453,6 +503,9 @@ public: type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HEAPTYPE; type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; +#if PY_MAJOR_VERSION < 3 + type->ht_type.tp_flags |= Py_TPFLAGS_CHECKTYPES; +#endif type->ht_type.tp_as_number = &type->as_number; type->ht_type.tp_as_sequence = &type->as_sequence; type->ht_type.tp_as_mapping = &type->as_mapping; @@ -482,7 +535,12 @@ protected: /* Allocate a metaclass on demand (for static properties) */ handle metaclass() { auto &ht_type = ((PyHeapTypeObject *) m_ptr)->ht_type; +#if PY_MAJOR_VERSION >= 3 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) { std::string name_ = std::string(ht_type.tp_name) + "_meta"; PyHeapTypeObject *type = (PyHeapTypeObject*) PyType_Type.tp_alloc(&PyType_Type, 0); @@ -490,7 +548,10 @@ protected: if (type == nullptr || name == nullptr) throw std::runtime_error("Internal error in custom_type::metaclass()"); Py_INCREF(name); - type->ht_name = type->ht_qualname = name; + type->ht_name = name; +#if PY_MAJOR_VERSION >= 3 + type->ht_qualname = name; +#endif type->ht_type.tp_name = strdup(name_.c_str()); type->ht_type.tp_base = &PyType_Type; type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HEAPTYPE; @@ -541,6 +602,9 @@ protected: void *get_buffer_data) { PyHeapTypeObject *type = (PyHeapTypeObject*) m_ptr; type->ht_type.tp_as_buffer = &type->as_buffer; +#if PY_MAJOR_VERSION < 3 + type->ht_type.tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER; +#endif type->as_buffer.bf_getbuffer = getbuffer; type->as_buffer.bf_releasebuffer = releasebuffer; auto info = ((detail::type_info *) capsule(attr("__pybind__"))); @@ -606,17 +670,19 @@ public: template class_ &def(const char *name_, Func&& f, Extra&&... extra) { - attr(name_) = cpp_function(std::forward(f), name(name_), - sibling(attr(name_)), is_method(), - std::forward(extra)...); + cpp_function cf(std::forward(f), name(name_), + sibling(attr(name_)), is_method(this), + std::forward(extra)...); + attr(cf.name()) = cf; return *this; } template class_ & def_static(const char *name_, Func f, Extra&&... extra) { - attr(name_) = cpp_function(std::forward(f), name(name_), - sibling(attr(name_)), - std::forward(extra)...); + cpp_function cf(std::forward(f), name(name_), + sibling(attr(name_)), + std::forward(extra)...); + attr(cf.name()) = cf; return *this; } @@ -654,9 +720,9 @@ public: class_ &def_readwrite(const char *name, D C::*pm, Extra&&... extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, return_value_policy::reference_internal, - is_method(), extra...), + is_method(this), extra...), fset([pm](C &c, const D &value) { c.*pm = value; }, - is_method(), extra...); + is_method(this), extra...); def_property(name, fget, fset); return *this; } @@ -665,7 +731,7 @@ public: class_ &def_readonly(const char *name, const D C::*pm, Extra&& ...extra) { cpp_function fget([pm](const C &c) -> const D &{ return c.*pm; }, return_value_policy::reference_internal, - is_method(), std::forward(extra)...); + is_method(this), std::forward(extra)...); def_property_readonly(name, fget); return *this; } diff --git a/include/pybind/pytypes.h b/include/pybind/pytypes.h index 5aff796c..e9a60e9c 100644 --- a/include/pybind/pytypes.h +++ b/include/pybind/pytypes.h @@ -213,10 +213,30 @@ class str : public object { public: PYBIND_OBJECT_DEFAULT(str, object, PyUnicode_Check) str(const char *s) : object(PyUnicode_FromString(s), false) { } - operator const char *() const { return PyUnicode_AsUTF8(m_ptr); } + operator const char *() const { +#if PY_MAJOR_VERSION >= 3 + return PyUnicode_AsUTF8(m_ptr); +#else + m_temp = object(PyUnicode_AsUTF8String(m_ptr), false); + if (m_temp.ptr() == nullptr) + return nullptr; + return PyString_AsString(m_temp.ptr()); +#endif + } +private: +#if PY_MAJOR_VERSION < 3 + mutable object m_temp; +#endif }; -inline pybind::str handle::str() const { return pybind::str(PyObject_Str(m_ptr), false); } +inline pybind::str handle::str() const { + PyObject *str = PyObject_Str(m_ptr); +#if PY_MAJOR_VERSION < 3 + PyObject *unicode = PyUnicode_FromEncodedObject(str, "utf-8", nullptr); + Py_XDECREF(str); str = unicode; +#endif + return pybind::str(str, false); +} class bool_ : public object { public: @@ -252,7 +272,13 @@ public: m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); } bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const { - return PySlice_GetIndicesEx(m_ptr, length, start, stop, step, slicelength) == 0; + return PySlice_GetIndicesEx( +#if PY_MAJOR_VERSION >= 3 + m_ptr, +#else + (PySliceObject *) m_ptr, +#endif + length, start, stop, step, slicelength) == 0; } }; diff --git a/include/pybind/stl.h b/include/pybind/stl.h index 4b935cfa..bf1d8980 100644 --- a/include/pybind/stl.h +++ b/include/pybind/stl.h @@ -100,9 +100,10 @@ public: PYBIND_TYPE_CASTER(type, detail::descr("dict<") + key_conv::name() + detail::descr(", ") + value_conv::name() + detail::descr(">")); }; +NAMESPACE_END(detail) + inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; } -NAMESPACE_END(detail) NAMESPACE_END(pybind) #if defined(_MSC_VER)