diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d255ea88..f8b468ce8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/example/example2.cpp b/example/example2.cpp index 08fdd06a7..c4a352e73 100644 --- a/example/example2.cpp +++ b/example/example2.cpp @@ -9,6 +9,7 @@ */ #include "example.h" +#include class Example2 { public: diff --git a/example/example5.cpp b/example/example5.cpp index 738f9f4b2..c601ed71a 100644 --- a/example/example5.cpp +++ b/example/example5.cpp @@ -9,6 +9,7 @@ */ #include "example.h" +#include class Pet { @@ -73,6 +74,14 @@ void test_callback3(Example5 *ex, int value) { ex->callback(value); } +void test_callback4(const std::function &func) { + cout << "func(43) = " << func(43)<< std::endl; +} + +std::function test_callback5() { + return [](int i) { return i+1; }; +} + void init_ex5(py::module &m) { py::class_ 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_(m, "Example5") .def(py::init()); diff --git a/example/example5.py b/example/example5.py index b119de975..c645af209 100755 --- a/example/example5.py +++ b/example/example5.py @@ -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)) diff --git a/include/pybind/cast.h b/include/pybind/cast.h index 301304764..c59fbcfcb 100644 --- a/include/pybind/cast.h +++ b/include/pybind/cast.h @@ -12,7 +12,6 @@ #include #include -#include #include 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 { +template <> class type_caster { 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 { @@ -192,23 +191,6 @@ public: PYBIND_TYPE_CASTER(bool, "bool"); }; -template class type_caster> { -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) result.real, (T) result.imag); - return true; - } - static PyObject *cast(const std::complex &src, return_value_policy /* policy */, PyObject * /* parent */) { - return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); - } - PYBIND_TYPE_CASTER(std::complex, "complex"); -}; - template <> class type_caster { public: bool load(PyObject *src, bool) { @@ -265,83 +247,6 @@ protected: char *value; }; -template struct type_caster> { - typedef std::vector type; - typedef type_caster 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"); -}; - -template struct type_caster> { -public: - typedef std::map type; - typedef type_caster key_conv; - typedef type_caster 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 class type_caster> { typedef std::pair type; public: @@ -354,8 +259,8 @@ public: } static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent) { - PyObject *o1 = type_caster::type>::cast(src.first, policy, parent); - PyObject *o2 = type_caster::type>::cast(src.second, policy, parent); + PyObject *o1 = type_caster::type>::cast(src.first, policy, parent); + PyObject *o2 = type_caster::type>::cast(src.second, policy, parent); if (!o1 || !o2) { Py_XDECREF(o1); Py_XDECREF(o2); @@ -375,11 +280,11 @@ public: return type(first, second); } protected: - type_caster::type> first; - type_caster::type> second; + type_caster::type> first; + type_caster::type> second; }; -template class type_caster> { +template class type_caster> { typedef std::tuple type; public: enum { size = sizeof...(Tuple) }; @@ -394,7 +299,7 @@ public: static std::string name(const char **keywords = nullptr, const char **values = nullptr) { std::array names {{ - type_caster::type>::name()... + type_caster::type>::name()... }}; std::string result("("); int counter = 0; @@ -419,9 +324,9 @@ public: return call(std::forward(f), typename make_index_sequence::type()); } - template typename std::enable_if::value, detail::void_type>::type call(Func &&f) { + template typename std::enable_if::value, void_type>::type call(Func &&f) { call(std::forward(f), typename make_index_sequence::type()); - return detail::void_type(); + return void_type(); } operator type() { @@ -443,8 +348,9 @@ protected: if (PyTuple_Size(src) != size) return false; std::array results {{ - std::get(value).load(PyTuple_GetItem(src, Indices), convert)... + (PyTuple_GET_ITEM(src, Indices) != nullptr ? std::get(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 static PyObject *cast(const type &src, return_value_policy policy, PyObject *parent, index_sequence) { std::array results {{ - type_caster::type>::cast(std::get(src), policy, parent)... + type_caster::type>::cast(std::get(src), policy, parent)... }}; bool success = true; for (auto result : results) @@ -475,7 +381,7 @@ protected: } protected: - std::tuple::type>...> value; + std::tuple::type>...> value; }; /// Type caster for holder types like std::shared_ptr, etc. @@ -542,7 +448,7 @@ template inline object cast(const T &value, return_value_policy pol template inline T handle::cast() { return pybind::cast(m_ptr); } -template inline object handle::call(Args&&... args_) { +template inline object handle::call(Args&&... args_) { const size_t size = sizeof...(Args); std::array args{ { detail::type_caster::type>::cast( diff --git a/include/pybind/common.h b/include/pybind/common.h index e4a7d34ac..0ee266825 100644 --- a/include/pybind/common.h +++ b/include/pybind/common.h @@ -31,9 +31,7 @@ #include #include #include -#include #include -#include /// 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 +#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 struct format_descriptor { }; -#define DECL_FMT(t, n) template<> struct format_descriptor { 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, "Zf"); DECL_FMT(std::complex, "Zd"); -#undef DECL_FMT +#define PYBIND_DECL_FMT(t, n) template<> struct format_descriptor { 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 struct decay { typedef typename deca /// Helper type to replace 'void' in some expressions struct void_type { }; + NAMESPACE_END(detail) NAMESPACE_END(pybind) diff --git a/include/pybind/complex.h b/include/pybind/complex.h new file mode 100644 index 000000000..9d690b14f --- /dev/null +++ b/include/pybind/complex.h @@ -0,0 +1,40 @@ +/* + pybind/complex.h: Complex number support + + Copyright (c) 2015 Wenzel Jakob + + 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 +#include + +NAMESPACE_BEGIN(pybind) + +PYBIND_DECL_FMT(std::complex, "Zf"); +PYBIND_DECL_FMT(std::complex, "Zd"); + +NAMESPACE_BEGIN(detail) +template class type_caster> { +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) result.real, (T) result.imag); + return true; + } + + static PyObject *cast(const std::complex &src, return_value_policy /* policy */, PyObject * /* parent */) { + return PyComplex_FromDoubles((double) src.real(), (double) src.imag()); + } + + PYBIND_TYPE_CASTER(std::complex, "complex"); +}; +NAMESPACE_END(detail) +NAMESPACE_END(pybind) diff --git a/include/pybind/functional.h b/include/pybind/functional.h new file mode 100644 index 000000000..1979dea7d --- /dev/null +++ b/include/pybind/functional.h @@ -0,0 +1,45 @@ +/* + pybind/functional.h: std::function<> support + + Copyright (c) 2015 Wenzel Jakob + + 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 +#include + +NAMESPACE_BEGIN(pybind) +NAMESPACE_BEGIN(detail) + +template struct type_caster> { + typedef std::function 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(std::move(args)...)); + /* Visual studio 2015 parser issue: need parentheses around this expression */ + return (retval.template cast()); + }; + return true; + } + + template + static PyObject *cast(Func &&f_, return_value_policy policy, PyObject *) { + cpp_function f(std::forward(f_), policy); + f.inc_ref(); + return f.ptr(); + } + + PYBIND_TYPE_CASTER(type, "function<" + type_caster>::name() + " -> " + type_caster::type>::name() + ">"); +}; + +NAMESPACE_END(detail) +NAMESPACE_END(pybind) diff --git a/include/pybind/numpy.h b/include/pybind/numpy.h index 3c06854f0..d101013ba 100644 --- a/include/pybind/numpy.h +++ b/include/pybind/numpy.h @@ -10,7 +10,7 @@ #pragma once #include -#include +#include #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::value); return api.PyArray_FromAny(ptr, descr, 0, 0, @@ -158,7 +160,8 @@ template struct vectorize_helper { typename std::remove_reference::type f; - vectorize_helper(const Func &f) : f(f) { } + template + vectorize_helper(T&&f) : f(std::forward(f)) { } object operator()(array_dtype... args) { return run(args..., typename make_index_sequence::type()); diff --git a/include/pybind/pybind.h b/include/pybind/pybind.h index 64ae5a0dd..de496fbd9 100644 --- a/include/pybind/pybind.h +++ b/include/pybind/pybind.h @@ -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 +#include 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(*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(*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 + diff --git a/include/pybind/pytypes.h b/include/pybind/pytypes.h index 2586a8bad..2a9052fd8 100644 --- a/include/pybind/pytypes.h +++ b/include/pybind/pytypes.h @@ -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 operator T *() const { T * result = static_cast(PyCapsule_GetPointer(m_ptr, nullptr)); if (!result) throw std::runtime_error("Unable to extract capsule contents!"); diff --git a/include/pybind/stl.h b/include/pybind/stl.h new file mode 100644 index 000000000..90c583b0a --- /dev/null +++ b/include/pybind/stl.h @@ -0,0 +1,109 @@ +/* + pybind/complex.h: Complex number support + + Copyright (c) 2015 Wenzel Jakob + + 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 +#include +#include + + +#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 struct type_caster> { + typedef std::vector type; + typedef type_caster 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"); +}; + +template struct type_caster> { +public: + typedef std::map type; + typedef type_caster key_conv; + typedef type_caster 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 diff --git a/tools/mkdoc.py b/tools/mkdoc.py index 4f4757503..d1b61c9ba 100644 --- a/tools/mkdoc.py +++ b/tools/mkdoc.py @@ -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 +''')