added new type pybind11::bytes, cleanup of various macros (fixes #49)

This commit is contained in:
Wenzel Jakob 2016-01-17 22:36:37 +01:00
parent 2dfbadee5d
commit 27e8e1066b
11 changed files with 135 additions and 103 deletions

View File

@ -801,9 +801,9 @@ For instance, the following statement iterates over a Python ``dict``:
} }
Available types include :class:`handle`, :class:`object`, :class:`bool_`, Available types include :class:`handle`, :class:`object`, :class:`bool_`,
:class:`int_`, :class:`float_`, :class:`str`, :class:`tuple`, :class:`list`, :class:`int_`, :class:`float_`, :class:`str`, :class:`bytes`, :class:`tuple`,
:class:`dict`, :class:`slice`, :class:`capsule`, :class:`function`, :class:`list`, :class:`dict`, :class:`slice`, :class:`capsule`,
:class:`buffer`, :class:`array`, and :class:`array_t`. :class:`function`, :class:`buffer`, :class:`array`, and :class:`array_t`.
In this kind of mixed code, it is often necessary to convert arbitrary C++ In this kind of mixed code, it is often necessary to convert arbitrary C++
types to Python, which can be done using :func:`cast`: types to Python, which can be done using :func:`cast`:

View File

@ -72,24 +72,12 @@ public:
std::cout << "key: " << item.first << ", value=" << item.second << std::endl; std::cout << "key: " << item.first << ", value=" << item.second << std::endl;
} }
/* STL data types are automatically casted from Python */ /* Easily iterate over a set using a C++11 range-based for loop */
void print_dict_2(const std::map<std::string, std::string> &dict) {
for (auto item : dict)
std::cout << "key: " << item.first << ", value=" << item.second << std::endl;
}
/* Easily iterate over a setionary using a C++11 range-based for loop */
void print_set(py::set set) { void print_set(py::set set) {
for (auto item : set) for (auto item : set)
std::cout << "key: " << item << std::endl; std::cout << "key: " << item << std::endl;
} }
/* STL data types are automatically casted from Python */
void print_set_2(const std::set<std::string> &set) {
for (auto item : set)
std::cout << "key: " << item << std::endl;
}
/* Easily iterate over a list using a C++11 range-based for loop */ /* Easily iterate over a list using a C++11 range-based for loop */
void print_list(py::list list) { void print_list(py::list list) {
int index = 0; int index = 0;
@ -97,7 +85,19 @@ public:
std::cout << "list item " << index++ << ": " << item << std::endl; std::cout << "list item " << index++ << ": " << item << std::endl;
} }
/* STL data types are automatically casted from Python */ /* STL data types (such as maps) are automatically casted from Python */
void print_dict_2(const std::map<std::string, std::string> &dict) {
for (auto item : dict)
std::cout << "key: " << item.first << ", value=" << item.second << std::endl;
}
/* STL data types (such as sets) are automatically casted from Python */
void print_set_2(const std::set<std::string> &set) {
for (auto item : set)
std::cout << "key: " << item << std::endl;
}
/* STL data types (such as vectors) are automatically casted from Python */
void print_list_2(std::vector<std::string> &list) { void print_list_2(std::vector<std::string> &list) {
int index = 0; int index = 0;
for (auto item : list) for (auto item : list)

View File

@ -1,5 +1,5 @@
/* /*
example/example4.cpp -- global constants and functions, enumerations example/example4.cpp -- global constants and functions, enumerations, raw byte strings
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch> Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
@ -40,6 +40,17 @@ float test_function3(int i) {
return i / 2.f; return i / 2.f;
} }
py::bytes return_bytes() {
const char *data = "\x01\x00\x02\x00";
return py::bytes(std::string(data, 4));
}
void print_bytes(py::bytes bytes) {
std::string value = (std::string) bytes;
for (size_t i = 0; i < value.length(); ++i)
std::cout << "bytes[" << i << "]=" << (int) value[i] << std::endl;
}
void init_ex4(py::module &m) { void init_ex4(py::module &m) {
m.def("test_function", &test_function1); m.def("test_function", &test_function1);
m.def("test_function", &test_function2); m.def("test_function", &test_function2);
@ -57,4 +68,7 @@ void init_ex4(py::module &m) {
.value("EFirstMode", Example4::EFirstMode) .value("EFirstMode", Example4::EFirstMode)
.value("ESecondMode", Example4::ESecondMode) .value("ESecondMode", Example4::ESecondMode)
.export_values(); .export_values();
m.def("return_bytes", &return_bytes);
m.def("print_bytes", &print_bytes);
} }

View File

@ -8,6 +8,8 @@ from example import some_constant
from example import EMyEnumeration from example import EMyEnumeration
from example import EFirstEntry from example import EFirstEntry
from example import Example4 from example import Example4
from example import return_bytes
from example import print_bytes
print(EMyEnumeration) print(EMyEnumeration)
print(EMyEnumeration.EFirstEntry) print(EMyEnumeration.EFirstEntry)
@ -23,3 +25,5 @@ print(Example4.EMode)
print(Example4.EMode.EFirstMode) print(Example4.EMode.EFirstMode)
print(Example4.EFirstMode) print(Example4.EFirstMode)
Example4.test_function(Example4.EFirstMode) Example4.test_function(Example4.EFirstMode)
print_bytes(return_bytes())

View File

@ -14,3 +14,7 @@ None
EMode.EFirstMode EMode.EFirstMode
EMode.EFirstMode EMode.EFirstMode
Example4::test_function(enum=1) Example4::test_function(enum=1)
bytes[0]=1
bytes[1]=0
bytes[2]=2
bytes[3]=0

View File

@ -21,6 +21,7 @@ def sanitize(lines):
line = shorten_floats.sub(r'\1', line) line = shorten_floats.sub(r'\1', line)
line = line.replace('__builtin__', 'builtins') line = line.replace('__builtin__', 'builtins')
line = line.replace('example.', '') line = line.replace('example.', '')
line = line.replace('unicode', 'str')
line = line.replace('method of builtins.PyCapsule instance', '') line = line.replace('method of builtins.PyCapsule instance', '')
line = line.strip() line = line.strip()
if sys.platform == 'win32': if sys.platform == 'win32':

View File

@ -19,12 +19,6 @@
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)
NAMESPACE_BEGIN(detail) NAMESPACE_BEGIN(detail)
#if PY_MAJOR_VERSION >= 3
#define PYBIND11_AS_STRING PyBytes_AsString
#else
#define PYBIND11_AS_STRING PyString_AsString
#endif
class type_caster_custom { class type_caster_custom {
public: public:
PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) { PYBIND11_NOINLINE type_caster_custom(const std::type_info *type_info) {
@ -185,9 +179,9 @@ public:
py_value = (py_type) PyLong_AsUnsignedLong(src); py_value = (py_type) PyLong_AsUnsignedLong(src);
} else { } else {
if (std::is_signed<T>::value) if (std::is_signed<T>::value)
py_value = (py_type) detail::PyLong_AsLongLong_(src); py_value = (py_type) PYBIND11_LONG_AS_LONGLONG(src);
else else
py_value = (py_type) detail::PyLong_AsUnsignedLongLong_(src); py_value = (py_type) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(src);
} }
if ((py_value == (py_type) -1 && PyErr_Occurred()) || if ((py_value == (py_type) -1 && PyErr_Occurred()) ||
@ -265,35 +259,41 @@ public:
template <> class type_caster<std::string> { template <> class type_caster<std::string> {
public: public:
bool load(PyObject *src, bool) { bool load(PyObject *src, bool) {
#if PY_MAJOR_VERSION < 3 object temp;
if (PyString_Check(src)) { value = PyString_AsString(src); return true; } PyObject *load_src = src;
#endif if (PyUnicode_Check(src)) {
object temp(PyUnicode_AsUTF8String(src), false); temp = object(PyUnicode_AsUTF8String(src), false);
const char *ptr = nullptr; if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
if (temp) load_src = temp.ptr();
ptr = PYBIND11_AS_STRING(temp.ptr()); }
if (!ptr) { PyErr_Clear(); return false; } char *buffer;
value = ptr; ssize_t length;
int err = PYBIND11_BYTES_AS_STRING_AND_SIZE(load_src, &buffer, &length);
if (err == -1) { PyErr_Clear(); return false; } // TypeError
value = std::string(buffer, length);
return true; return true;
} }
static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) { static PyObject *cast(const std::string &src, return_value_policy /* policy */, PyObject * /* parent */) {
return PyUnicode_FromString(src.c_str()); return PyUnicode_FromStringAndSize(src.c_str(), src.length());
} }
PYBIND11_TYPE_CASTER(std::string, _("str"));
PYBIND11_TYPE_CASTER(std::string, _(PYBIND11_STRING_NAME));
}; };
template <> class type_caster<char> { template <> class type_caster<char> {
public: public:
bool load(PyObject *src, bool) { bool load(PyObject *src, bool) {
#if PY_MAJOR_VERSION < 3 object temp;
if (PyString_Check(src)) { value = PyString_AsString(src); return true; } PyObject *load_src = src;
#endif if (PyUnicode_Check(src)) {
object temp(PyUnicode_AsUTF8String(src), false); temp = object(PyUnicode_AsUTF8String(src), false);
const char *ptr = nullptr; if (!temp) { PyErr_Clear(); return false; } // UnicodeEncodeError
if (temp) load_src = temp.ptr();
ptr = PYBIND11_AS_STRING(temp.ptr()); }
if (!ptr) { PyErr_Clear(); return false; } const char *ptr = PYBIND11_BYTES_AS_STRING(load_src);
value = ptr; if (!ptr) { PyErr_Clear(); return false; } // TypeError
value = std::string(ptr);
return true; return true;
} }
@ -306,10 +306,10 @@ public:
return PyUnicode_DecodeLatin1(str, 1, nullptr); return PyUnicode_DecodeLatin1(str, 1, nullptr);
} }
static PYBIND11_DESCR name() { return type_descr(_("str")); }
operator char*() { return (char *) value.c_str(); } operator char*() { return (char *) value.c_str(); }
operator char() { if (value.length() > 0) return value[0]; else return '\0'; } operator char() { if (value.length() > 0) return value[0]; else return '\0'; }
static PYBIND11_DESCR name() { return type_descr(_(PYBIND11_STRING_NAME)); }
protected: protected:
std::string value; std::string value;
}; };

View File

@ -85,6 +85,29 @@
} \ } \
PyObject *pybind11_init() PyObject *pybind11_init()
#if PY_MAJOR_VERSION >= 3 /// Compatibility macros for various Python versions
#define PYBIND11_BYTES_CHECK PyBytes_Check
#define PYBIND11_BYTES_FROM_STRING PyBytes_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyBytes_FromStringAndSize
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyBytes_AsStringAndSize
#define PYBIND11_BYTES_AS_STRING PyBytes_AsString
#define PYBIND11_LONG_CHECK(o) PyLong_Check(o)
#define PYBIND11_LONG_AS_LONGLONG(o) PyLong_AsLongLong(o)
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) PyLong_AsUnsignedLongLong(o)
#define PYBIND11_STRING_NAME "str"
#define PYBIND11_SLICE_OBJECT PyObject
#else
#define PYBIND11_BYTES_CHECK PyString_Check
#define PYBIND11_BYTES_FROM_STRING PyString_FromString
#define PYBIND11_BYTES_FROM_STRING_AND_SIZE PyString_FromStringAndSize
#define PYBIND11_BYTES_AS_STRING_AND_SIZE PyString_AsStringAndSize
#define PYBIND11_BYTES_AS_STRING PyString_AsString
#define PYBIND11_LONG_CHECK(o) (PyInt_Check(o) || PyLong_Check(o))
#define PYBIND11_LONG_AS_LONGLONG(o) (PyInt_Check(o) ? (long long) PyLong_AsLong(o) : PyLong_AsLongLong(o))
#define PYBIND11_LONG_AS_UNSIGNED_LONGLONG(o) (PyInt_Check(o) ? (unsigned long long) PyLong_AsUnsignedLong(o) : PyLong_AsUnsignedLongLong(o))
#define PYBIND11_STRING_NAME "unicode"
#define PYBIND11_SLICE_OBJECT PySliceObject
#endif
NAMESPACE_BEGIN(pybind11) NAMESPACE_BEGIN(pybind11)

View File

@ -337,7 +337,7 @@ private:
if (a.descr) if (a.descr)
a.descr = strdup(a.descr); a.descr = strdup(a.descr);
else if (a.value) else if (a.value)
a.descr = strdup(((object) handle(a.value).attr("__repr__")).call().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;
@ -367,7 +367,7 @@ private:
} else if (c == '%') { } else if (c == '%') {
const std::type_info *t = types[type_index++]; const std::type_info *t = types[type_index++];
if (!t) if (!t)
throw std::runtime_error("Internal error while generating 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 += it->second.type->tp_name;
@ -381,7 +381,7 @@ private:
} }
} }
if (type_depth != 0 && types[type_index ] != nullptr) if (type_depth != 0 && types[type_index ] != nullptr)
throw std::runtime_error("Internal error while generating type signature (2)"); throw std::runtime_error("Internal error while parsing type signature (2)");
#if !defined(PYBIND11_CPP14) #if !defined(PYBIND11_CPP14)
delete[] types; delete[] types;
@ -694,8 +694,8 @@ protected:
view->format = const_cast<char *>(info->format.c_str()); view->format = const_cast<char *>(info->format.c_str());
if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) { if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) {
view->ndim = info->ndim; view->ndim = info->ndim;
view->strides = (Py_ssize_t *)&info->strides[0]; view->strides = (ssize_t *) &info->strides[0];
view->shape = (Py_ssize_t *) &info->shape[0]; view->shape = (ssize_t *) &info->shape[0];
} }
Py_INCREF(view->obj); Py_INCREF(view->obj);
return 0; return 0;
@ -903,7 +903,7 @@ public:
void export_values() { void export_values() {
PyObject *dict = ((PyTypeObject *) this->m_ptr)->tp_dict; PyObject *dict = ((PyTypeObject *) this->m_ptr)->tp_dict;
PyObject *key, *value; PyObject *key, *value;
Py_ssize_t pos = 0; ssize_t pos = 0;
while (PyDict_Next(dict, &pos, &key, &value)) while (PyDict_Next(dict, &pos, &key, &value))
if (PyObject_IsInstance(value, this->m_ptr)) if (PyObject_IsInstance(value, this->m_ptr))
m_parent.attr(key) = value; m_parent.attr(key) = value;
@ -983,9 +983,10 @@ inline function get_overload(const void *this_ptr, const char *name) {
cache.insert(key); cache.insert(key);
return function(); return function();
} }
PyFrameObject *frame = PyThreadState_Get()->frame; PyFrameObject *frame = PyThreadState_Get()->frame;
pybind11::str caller = pybind11::handle(frame->f_code->co_name).str(); pybind11::str caller = pybind11::handle(frame->f_code->co_name).str();
if (strcmp((const char *) caller, name) == 0) if ((std::string) caller == name)
return function(); return function();
return overload; return overload;
} }

View File

@ -215,30 +215,6 @@ private:
ssize_t pos = 0; ssize_t pos = 0;
}; };
#if PY_MAJOR_VERSION >= 3
inline long long PyLong_AsLongLong_(PyObject *o) { return PyLong_AsLongLong(o); }
inline unsigned long long PyLong_AsUnsignedLongLong_(PyObject *o) { return PyLong_AsUnsignedLongLong(o); }
inline bool PyLong_Check_(PyObject *o) { return PyLong_Check(o); }
#else
inline long long PyLong_AsLongLong_(PyObject *o) {
if (PyInt_Check(o)) /// workaround: PyLong_AsLongLong doesn't accept 'int' on Python 2.x
return (long long) PyLong_AsLong(o);
else
return PyLong_AsLongLong(o);
}
inline unsigned long long PyLong_AsUnsignedLongLong_(PyObject *o) {
if (PyInt_Check(o)) /// workaround: PyLong_AsUnsignedLongLong doesn't accept 'int' on Python 2.x
return (unsigned long long) PyLong_AsUnsignedLong(o);
else
return PyLong_AsUnsignedLongLong(o);
}
inline bool PyLong_Check_(PyObject *o) {
return PyInt_Check(o) || PyLong_Check(o);
}
#endif
NAMESPACE_END(detail) NAMESPACE_END(detail)
inline detail::accessor handle::operator[](handle key) const { return detail::accessor(ptr(), key.ptr(), false); } inline detail::accessor handle::operator[](handle key) const { return detail::accessor(ptr(), key.ptr(), false); }
@ -266,21 +242,18 @@ inline iterator handle::end() const { return iterator(nullptr); }
class str : public object { class str : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check) PYBIND11_OBJECT_DEFAULT(str, object, PyUnicode_Check)
str(const char *s) : object(PyUnicode_FromString(s), false) { } str(const std::string &s) : object(PyUnicode_FromStringAndSize(s.c_str(), s.length()), false) { }
operator const char *() const {
operator std::string() const {
#if PY_MAJOR_VERSION >= 3 #if PY_MAJOR_VERSION >= 3
return PyUnicode_AsUTF8(m_ptr); return PyUnicode_AsUTF8(m_ptr);
#else #else
m_temp = object(PyUnicode_AsUTF8String(m_ptr), false); object temp(PyUnicode_AsUTF8String(m_ptr), false);
if (m_temp.ptr() == nullptr) if (temp.ptr() == nullptr)
return nullptr; throw std::runtime_error("Unable to extract string contents!");
return PyString_AsString(m_temp.ptr()); return PyString_AsString(temp.ptr());
#endif #endif
} }
private:
#if PY_MAJOR_VERSION < 3
mutable object m_temp;
#endif
}; };
inline pybind11::str handle::str() const { inline pybind11::str handle::str() const {
@ -292,6 +265,23 @@ inline pybind11::str handle::str() const {
return pybind11::str(str, false); return pybind11::str(str, false);
} }
class bytes : public object {
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) { }
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!");
return std::string(buffer, length);
}
};
class bool_ : public object { class bool_ : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(bool_, object, PyBool_Check) PYBIND11_OBJECT_DEFAULT(bool_, object, PyBool_Check)
@ -301,7 +291,7 @@ public:
class int_ : public object { class int_ : public object {
public: public:
PYBIND11_OBJECT_DEFAULT(int_, object, detail::PyLong_Check_) PYBIND11_OBJECT_DEFAULT(int_, object, PYBIND11_LONG_CHECK)
template <typename T, template <typename T,
typename std::enable_if<std::is_integral<T>::value, int>::type = 0> typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
int_(T value) { int_(T value) {
@ -328,9 +318,9 @@ public:
return (T) PyLong_AsUnsignedLong(m_ptr); return (T) PyLong_AsUnsignedLong(m_ptr);
} else { } else {
if (std::is_signed<T>::value) if (std::is_signed<T>::value)
return (T) detail::PyLong_AsLongLong_(m_ptr); return (T) PYBIND11_LONG_AS_LONGLONG(m_ptr);
else else
return (T) detail::PyLong_AsUnsignedLongLong_(m_ptr); return (T) PYBIND11_LONG_AS_UNSIGNED_LONGLONG(m_ptr);
} }
} }
}; };
@ -352,13 +342,8 @@ public:
m_ptr = PySlice_New(start.ptr(), stop.ptr(), step.ptr()); 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 { bool compute(ssize_t length, ssize_t *start, ssize_t *stop, ssize_t *step, ssize_t *slicelength) const {
return PySlice_GetIndicesEx( return PySlice_GetIndicesEx((PYBIND11_SLICE_OBJECT *) m_ptr, length,
#if PY_MAJOR_VERSION >= 3 start, stop, step, slicelength) == 0;
m_ptr,
#else
(PySliceObject *) m_ptr,
#endif
length, start, stop, step, slicelength) == 0;
} }
}; };
@ -377,7 +362,7 @@ public:
class tuple : public object { class tuple : public object {
public: public:
PYBIND11_OBJECT(tuple, object, PyTuple_Check) PYBIND11_OBJECT(tuple, object, PyTuple_Check)
tuple(size_t size = 0) : object(PyTuple_New((Py_ssize_t) size), false) { } tuple(size_t size = 0) : object(PyTuple_New((ssize_t) size), false) { }
size_t size() const { return (size_t) PyTuple_Size(m_ptr); } 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); } detail::tuple_accessor operator[](size_t index) const { return detail::tuple_accessor(ptr(), index); }
}; };
@ -466,11 +451,11 @@ inline std::string error_string() {
return ""; return "";
if (tstate->curexc_type) { if (tstate->curexc_type) {
errorString += (const char *) handle(tstate->curexc_type).str(); errorString += (std::string) handle(tstate->curexc_type).str();
errorString += ": "; errorString += ": ";
} }
if (tstate->curexc_value) if (tstate->curexc_value)
errorString += (const char *) handle(tstate->curexc_value).str(); errorString += (std::string) handle(tstate->curexc_value).str();
return errorString; return errorString;
} }

View File

@ -131,7 +131,7 @@ public:
NAMESPACE_END(detail) NAMESPACE_END(detail)
inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; } inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (std::string) obj.str(); return os; }
NAMESPACE_END(pybind11) NAMESPACE_END(pybind11)