nicer code separation, cleanup logic, std::function type caster

This commit is contained in:
Wenzel Jakob 2015-07-30 15:29:00 +02:00
parent a576e6a8ca
commit 281aa0e668
13 changed files with 296 additions and 132 deletions

View File

@ -22,7 +22,10 @@ find_package(PythonInterp 3 REQUIRED)
string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-unsequenced")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unsequenced")
endif()
if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -flto")
endif()

View File

@ -9,6 +9,7 @@
*/
#include "example.h"
#include <pybind/stl.h>
class Example2 {
public:

View File

@ -9,6 +9,7 @@
*/
#include "example.h"
#include <pybind/functional.h>
class Pet {
@ -73,6 +74,14 @@ void test_callback3(Example5 *ex, int value) {
ex->callback(value);
}
void test_callback4(const std::function<int(int)> &func) {
cout << "func(43) = " << func(43)<< std::endl;
}
std::function<int(int)> test_callback5() {
return [](int i) { return i+1; };
}
void init_ex5(py::module &m) {
py::class_<Pet> pet_class(m, "Pet");
pet_class
@ -89,6 +98,8 @@ void init_ex5(py::module &m) {
m.def("test_callback1", &test_callback1);
m.def("test_callback2", &test_callback2);
m.def("test_callback3", &test_callback3);
m.def("test_callback4", &test_callback4);
m.def("test_callback5", &test_callback5);
py::class_<Example5>(m, "Example5")
.def(py::init<py::object, int>());

View File

@ -22,6 +22,8 @@ except Exception as e:
from example import test_callback1
from example import test_callback2
from example import test_callback3
from example import test_callback4
from example import test_callback5
from example import Example5
def func1():
@ -43,3 +45,7 @@ print(test_callback2(func2))
callback = MyCallback(3)
test_callback3(callback, 4)
test_callback4(lambda i: i+1)
f = test_callback5()
print("func(43) = %i" % f(43))

View File

@ -12,7 +12,6 @@
#include <pybind/pytypes.h>
#include <pybind/typeid.h>
#include <map>
#include <array>
NAMESPACE_BEGIN(pybind)
@ -167,14 +166,14 @@ PYBIND_TYPE_CASTER_NUMBER(size_t, size_t, PyLong_AsSize_t, PyLong_FromSize_t)
PYBIND_TYPE_CASTER_NUMBER(float, float, PyFloat_AsDouble, PyFloat_FromDouble)
PYBIND_TYPE_CASTER_NUMBER(double, double, PyFloat_AsDouble, PyFloat_FromDouble)
template <> class type_caster<detail::void_type> {
template <> class type_caster<void_type> {
public:
bool load(PyObject *, bool) { return true; }
static PyObject *cast(detail::void_type, return_value_policy /* policy */, PyObject * /* parent */) {
static PyObject *cast(void_type, return_value_policy /* policy */, PyObject * /* parent */) {
Py_INCREF(Py_None);
return Py_None;
}
PYBIND_TYPE_CASTER(detail::void_type, "None");
PYBIND_TYPE_CASTER(void_type, "None");
};
template <> class type_caster<bool> {
@ -192,23 +191,6 @@ public:
PYBIND_TYPE_CASTER(bool, "bool");
};
template <typename T> class type_caster<std::complex<T>> {
public:
bool load(PyObject *src, bool) {
Py_complex result = PyComplex_AsCComplex(src);
if (result.real == -1.0 && PyErr_Occurred()) {
PyErr_Clear();
return false;
}
value = std::complex<T>((T) result.real, (T) result.imag);
return true;
}
static PyObject *cast(const std::complex<T> &src, return_value_policy /* policy */, PyObject * /* parent */) {
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
}
PYBIND_TYPE_CASTER(std::complex<T>, "complex");
};
template <> class type_caster<std::string> {
public:
bool load(PyObject *src, bool) {
@ -265,83 +247,6 @@ protected:
char *value;
};
template <typename Value> struct type_caster<std::vector<Value>> {
typedef std::vector<Value> type;
typedef type_caster<Value> value_conv;
public:
bool load(PyObject *src, bool convert) {
if (!PyList_Check(src))
return false;
size_t size = (size_t) PyList_GET_SIZE(src);
value.reserve(size);
value.clear();
for (size_t i=0; i<size; ++i) {
value_conv conv;
if (!conv.load(PyList_GetItem(src, (ssize_t) i), convert))
return false;
value.push_back((Value) conv);
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *list = PyList_New(src.size());
size_t index = 0;
for (auto const &value: src) {
PyObject *value_ = value_conv::cast(value, policy, parent);
if (!value_) {
Py_DECREF(list);
return nullptr;
}
PyList_SetItem(list, index++, value_);
}
return list;
}
PYBIND_TYPE_CASTER(type, "list<" + value_conv::name() + ">");
};
template <typename Key, typename Value> struct type_caster<std::map<Key, Value>> {
public:
typedef std::map<Key, Value> type;
typedef type_caster<Key> key_conv;
typedef type_caster<Value> value_conv;
bool load(PyObject *src, bool convert) {
if (!PyDict_Check(src))
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))
return false;
value[kconv] = vconv;
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *dict = PyDict_New();
for (auto const &kv: src) {
PyObject *key = key_conv::cast(kv.first, policy, parent);
PyObject *value = value_conv::cast(kv.second, policy, parent);
if (!key || !value || PyDict_SetItem(dict, key, value) < 0) {
Py_XDECREF(key);
Py_XDECREF(value);
Py_DECREF(dict);
return nullptr;
}
Py_DECREF(key);
Py_DECREF(value);
}
return dict;
}
PYBIND_TYPE_CASTER(type, "dict<" + key_conv::name() + ", " + value_conv::name() + ">");
};
template <typename T1, typename T2> class type_caster<std::pair<T1, T2>> {
typedef std::pair<T1, T2> type;
public:
@ -354,8 +259,8 @@ public:
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *o1 = type_caster<typename detail::decay<T1>::type>::cast(src.first, policy, parent);
PyObject *o2 = type_caster<typename detail::decay<T2>::type>::cast(src.second, policy, parent);
PyObject *o1 = type_caster<typename decay<T1>::type>::cast(src.first, policy, parent);
PyObject *o2 = type_caster<typename decay<T2>::type>::cast(src.second, policy, parent);
if (!o1 || !o2) {
Py_XDECREF(o1);
Py_XDECREF(o2);
@ -375,8 +280,8 @@ public:
return type(first, second);
}
protected:
type_caster<typename detail::decay<T1>::type> first;
type_caster<typename detail::decay<T2>::type> second;
type_caster<typename decay<T1>::type> first;
type_caster<typename decay<T2>::type> second;
};
template <typename... Tuple> class type_caster<std::tuple<Tuple...>> {
@ -394,7 +299,7 @@ public:
static std::string name(const char **keywords = nullptr, const char **values = nullptr) {
std::array<std::string, size> names {{
type_caster<typename detail::decay<Tuple>::type>::name()...
type_caster<typename decay<Tuple>::type>::name()...
}};
std::string result("(");
int counter = 0;
@ -419,9 +324,9 @@ public:
return call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
}
template <typename ReturnValue, typename Func> typename std::enable_if<std::is_void<ReturnValue>::value, detail::void_type>::type call(Func &&f) {
template <typename ReturnValue, typename Func> typename std::enable_if<std::is_void<ReturnValue>::value, void_type>::type call(Func &&f) {
call<ReturnValue>(std::forward<Func>(f), typename make_index_sequence<sizeof...(Tuple)>::type());
return detail::void_type();
return void_type();
}
operator type() {
@ -443,8 +348,9 @@ protected:
if (PyTuple_Size(src) != size)
return false;
std::array<bool, size> results {{
std::get<Indices>(value).load(PyTuple_GetItem(src, Indices), convert)...
(PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get<Indices>(value).load(PyTuple_GET_ITEM(src, Indices), convert) : false)...
}};
(void) convert; /* avoid a warning when the tuple is empty */
for (bool r : results)
if (!r)
return false;
@ -454,7 +360,7 @@ protected:
/* Implementation: Convert a C++ tuple into a Python tuple */
template <size_t ... Indices> static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent, index_sequence<Indices...>) {
std::array<PyObject *, size> results {{
type_caster<typename detail::decay<Tuple>::type>::cast(std::get<Indices>(src), policy, parent)...
type_caster<typename decay<Tuple>::type>::cast(std::get<Indices>(src), policy, parent)...
}};
bool success = true;
for (auto result : results)
@ -475,7 +381,7 @@ protected:
}
protected:
std::tuple<type_caster<typename detail::decay<Tuple>::type>...> value;
std::tuple<type_caster<typename decay<Tuple>::type>...> value;
};
/// Type caster for holder types like std::shared_ptr, etc.

View File

@ -31,9 +31,7 @@
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <iostream>
#include <memory>
#include <complex>
/// Include Python header, disable linking to pythonX_d.lib on Windows in debug mode
#if defined(_MSC_VER)
@ -46,6 +44,15 @@
#endif
#endif
#include <Python.h>
#ifdef isalnum
#undef isalnum
#undef isalpha
#undef islower
#undef isspace
#undef isupper
#undef tolower
#undef toupper
#endif
#if defined(_MSC_VER)
#if defined(_DEBUG_MARKER)
#define _DEBUG
@ -79,12 +86,10 @@ enum class return_value_policy : int {
/// Format strings for basic number types
template <typename type> struct format_descriptor { };
#define DECL_FMT(t, n) template<> struct format_descriptor<t> { static std::string value() { return n; }; };
DECL_FMT(int8_t, "b"); DECL_FMT(uint8_t, "B"); DECL_FMT(int16_t, "h"); DECL_FMT(uint16_t, "H");
DECL_FMT(int32_t, "i"); DECL_FMT(uint32_t, "I"); DECL_FMT(int64_t, "q"); DECL_FMT(uint64_t, "Q");
DECL_FMT(float, "f"); DECL_FMT(double, "d"); DECL_FMT(bool, "?");
DECL_FMT(std::complex<float>, "Zf"); DECL_FMT(std::complex<double>, "Zd");
#undef DECL_FMT
#define PYBIND_DECL_FMT(t, n) template<> struct format_descriptor<t> { static std::string value() { return n; }; };
PYBIND_DECL_FMT(int8_t, "b"); PYBIND_DECL_FMT(uint8_t, "B"); PYBIND_DECL_FMT(int16_t, "h"); PYBIND_DECL_FMT(uint16_t, "H");
PYBIND_DECL_FMT(int32_t, "i"); PYBIND_DECL_FMT(uint32_t, "I"); PYBIND_DECL_FMT(int64_t, "q"); PYBIND_DECL_FMT(uint64_t, "Q");
PYBIND_DECL_FMT(float, "f"); PYBIND_DECL_FMT(double, "d"); PYBIND_DECL_FMT(bool, "?");
/// Information record describing a Python buffer object
struct buffer_info {
@ -162,5 +167,6 @@ template <typename T, size_t N> struct decay<T[N]> { typedef typename deca
/// Helper type to replace 'void' in some expressions
struct void_type { };
NAMESPACE_END(detail)
NAMESPACE_END(pybind)

40
include/pybind/complex.h Normal file
View File

@ -0,0 +1,40 @@
/*
pybind/complex.h: Complex number support
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include <pybind/pybind.h>
#include <complex>
NAMESPACE_BEGIN(pybind)
PYBIND_DECL_FMT(std::complex<float>, "Zf");
PYBIND_DECL_FMT(std::complex<double>, "Zd");
NAMESPACE_BEGIN(detail)
template <typename T> class type_caster<std::complex<T>> {
public:
bool load(PyObject *src, bool) {
Py_complex result = PyComplex_AsCComplex(src);
if (result.real == -1.0 && PyErr_Occurred()) {
PyErr_Clear();
return false;
}
value = std::complex<T>((T) result.real, (T) result.imag);
return true;
}
static PyObject *cast(const std::complex<T> &src, return_value_policy /* policy */, PyObject * /* parent */) {
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
}
PYBIND_TYPE_CASTER(std::complex<T>, "complex");
};
NAMESPACE_END(detail)
NAMESPACE_END(pybind)

View File

@ -0,0 +1,45 @@
/*
pybind/functional.h: std::function<> support
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include <pybind/pybind.h>
#include <functional>
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(detail)
template <typename Return, typename... Args> struct type_caster<std::function<Return(Args...)>> {
typedef std::function<Return(Args...)> type;
public:
bool load(PyObject *src_, bool) {
if (!PyFunction_Check(src_))
return false;
object src(src_, true);
value = [src](Args... args) -> Return {
object retval(pybind::handle(src).call<Args...>(std::move(args)...));
/* Visual studio 2015 parser issue: need parentheses around this expression */
return (retval.template cast<Return>());
};
return true;
}
template <typename Func>
static PyObject *cast(Func &&f_, return_value_policy policy, PyObject *) {
cpp_function f(std::forward<Func>(f_), policy);
f.inc_ref();
return f.ptr();
}
PYBIND_TYPE_CASTER(type, "function<" + type_caster<std::tuple<Args...>>::name() + " -> " + type_caster<typename decay<Return>::type>::name() + ">");
};
NAMESPACE_END(detail)
NAMESPACE_END(pybind)

View File

@ -10,7 +10,7 @@
#pragma once
#include <pybind/pybind.h>
#include <functional>
#include <pybind/complex.h>
#if defined(_MSC_VER)
#pragma warning(push)
@ -126,6 +126,8 @@ public:
array_dtype() : array() { }
static bool is_non_null(PyObject *ptr) { return ptr != nullptr; }
PyObject *ensure(PyObject *ptr) {
if (ptr == nullptr)
return nullptr;
API &api = lookup_api();
PyObject *descr = api.PyArray_DescrFromType(npy_format_descriptor<T>::value);
return api.PyArray_FromAny(ptr, descr, 0, 0,
@ -158,7 +160,8 @@ template <typename Func, typename Return, typename... Args>
struct vectorize_helper {
typename std::remove_reference<Func>::type f;
vectorize_helper(const Func &f) : f(f) { }
template <typename T>
vectorize_helper(T&&f) : f(std::forward<T>(f)) { }
object operator()(array_dtype<Args>... args) {
return run(args..., typename make_index_sequence<sizeof...(Args)>::type());

View File

@ -16,9 +16,15 @@
#pragma warning(disable: 4996) // warning C4996: The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name
#pragma warning(disable: 4100) // warning C4100: Unreferenced formal parameter
#pragma warning(disable: 4512) // warning C4512: Assignment operator was implicitly defined as deleted
#elif defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
#include <pybind/cast.h>
#include <iostream>
NAMESPACE_BEGIN(pybind)
@ -57,7 +63,8 @@ private:
struct function_entry {
const char *name = nullptr;
PyObject * (*impl) (function_entry *, PyObject *, PyObject *, PyObject *);
void *data;
PyMethodDef *def;
void *data = nullptr;
bool is_constructor = false, is_method = false;
short keywords = 0;
return_value_policy policy = return_value_policy::automatic;
@ -304,6 +311,17 @@ private:
}
}
static void destruct(function_entry *entry) {
while (entry) {
delete entry->def;
operator delete(entry->data);
Py_XDECREF(entry->sibling);
function_entry *next = entry->next;
delete entry;
entry = next;
}
}
void initialize(function_entry *entry, int args) {
if (entry->name == nullptr)
entry->name = "";
@ -316,13 +334,13 @@ private:
entry->is_constructor = !strcmp(entry->name, "__init__");
if (!entry->sibling || !PyCFunction_Check(entry->sibling)) {
PyMethodDef *def = new PyMethodDef();
memset(def, 0, sizeof(PyMethodDef));
def->ml_name = entry->name;
def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule entry_capsule(entry);
m_ptr = PyCFunction_New(def, entry_capsule.ptr());
entry->def = new PyMethodDef();
memset(entry->def, 0, sizeof(PyMethodDef));
entry->def->ml_name = entry->name;
entry->def->ml_meth = reinterpret_cast<PyCFunction>(*dispatcher);
entry->def->ml_flags = METH_VARARGS | METH_KEYWORDS;
capsule entry_capsule(entry, [](PyObject *o) { destruct((function_entry *) PyCapsule_GetPointer(o, nullptr)); });
m_ptr = PyCFunction_New(entry->def, entry_capsule.ptr());
if (!m_ptr)
throw std::runtime_error("cpp_function::cpp_function(): Could not allocate function object");
} else {
@ -335,6 +353,7 @@ private:
parent->next = entry;
entry = backup;
}
std::string signatures;
int index = 0;
function_entry *it = entry;
@ -799,4 +818,7 @@ NAMESPACE_END(pybind)
#if defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__GNUG__)
#pragma GCC diagnostic pop
#endif

View File

@ -217,7 +217,6 @@ public:
};
inline pybind::str handle::str() const { return pybind::str(PyObject_Str(m_ptr), false); }
inline std::ostream &operator<<(std::ostream &os, const object &obj) { os << (const char *) obj.str(); return os; }
class bool_ : public object {
public:
@ -258,7 +257,8 @@ public:
class capsule : public object {
public:
PYBIND_OBJECT_DEFAULT(capsule, object, PyCapsule_CheckExact)
capsule(void *value) : object(PyCapsule_New(value, nullptr, nullptr), false) { }
capsule(PyObject *obj, bool borrowed) : object(obj, borrowed) { }
capsule(void *value, void (*destruct)(PyObject *) = nullptr) : object(PyCapsule_New(value, nullptr, destruct), false) { }
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!");

109
include/pybind/stl.h Normal file
View File

@ -0,0 +1,109 @@
/*
pybind/complex.h: Complex number support
Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch>
All rights reserved. Use of this source code is governed by a
BSD-style license that can be found in the LICENSE file.
*/
#pragma once
#include <pybind/pybind.h>
#include <map>
#include <iostream>
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4127) // warning C4127: Conditional expression is constant
#endif
NAMESPACE_BEGIN(pybind)
NAMESPACE_BEGIN(detail)
template <typename Value> struct type_caster<std::vector<Value>> {
typedef std::vector<Value> type;
typedef type_caster<Value> value_conv;
public:
bool load(PyObject *src, bool convert) {
if (!PyList_Check(src))
return false;
size_t size = (size_t) PyList_GET_SIZE(src);
value.reserve(size);
value.clear();
for (size_t i=0; i<size; ++i) {
value_conv conv;
if (!conv.load(PyList_GetItem(src, (ssize_t) i), convert))
return false;
value.push_back((Value) conv);
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *list = PyList_New(src.size());
size_t index = 0;
for (auto const &value: src) {
PyObject *value_ = value_conv::cast(value, policy, parent);
if (!value_) {
Py_DECREF(list);
return nullptr;
}
PyList_SetItem(list, index++, value_);
}
return list;
}
PYBIND_TYPE_CASTER(type, "list<" + value_conv::name() + ">");
};
template <typename Key, typename Value> struct type_caster<std::map<Key, Value>> {
public:
typedef std::map<Key, Value> type;
typedef type_caster<Key> key_conv;
typedef type_caster<Value> value_conv;
bool load(PyObject *src, bool convert) {
if (!PyDict_Check(src))
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))
return false;
value[kconv] = vconv;
}
return true;
}
static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) {
PyObject *dict = PyDict_New();
for (auto const &kv: src) {
PyObject *key = key_conv::cast(kv.first, policy, parent);
PyObject *value = value_conv::cast(kv.second, policy, parent);
if (!key || !value || PyDict_SetItem(dict, key, value) < 0) {
Py_XDECREF(key);
Py_XDECREF(value);
Py_DECREF(dict);
return nullptr;
}
Py_DECREF(key);
Py_DECREF(value);
}
return dict;
}
PYBIND_TYPE_CASTER(type, "dict<" + key_conv::name() + ", " + value_conv::name() + ">");
};
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)
#pragma warning(pop)
#endif

View File

@ -212,7 +212,13 @@ if __name__ == '__main__':
#define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3
#define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4
#define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4_##n5
#define DOC(...) __CAT2(__DOC, __VA_SIZE(__VA_ARGS__))(__VA_ARGS__)''')
#define DOC(...) __CAT2(__DOC, __VA_SIZE(__VA_ARGS__))(__VA_ARGS__)
#if defined(__GNUG__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
#endif
''')
output = []
for filename in filenames:
@ -226,3 +232,9 @@ if __name__ == '__main__':
output.sort()
for l in output:
print(l)
print('''
#if defined(__GNUG__)
#pragma GCC diagnostic pop
#endif
''')